Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Fri, 19 Dec 2025 16:59:45 +0000 en-US hourly 1 https://wordpress.org/?v=6.9 225069128 Exploring Multi-Brand Systems with Tokens and Composability https://frontendmasters.com/blog/exploring-multi-brand-systems-with-tokens-and-composability/ https://frontendmasters.com/blog/exploring-multi-brand-systems-with-tokens-and-composability/#respond Fri, 19 Dec 2025 16:59:44 +0000 https://frontendmasters.com/blog/?p=8041 Design systems aren’t just about keeping everything looking the same; they’re about making things flexible enough to handle whatever gets thrown at them.

When you’re juggling multiple brands or different use‑cases, a rigid component can feel more limiting than helpful. That’s where tokens, composition, and configuration come in. Tokens let you swap out brand colors and styles without rewriting code, composition gives you building blocks you can remix into new layouts, and configuration keeps all those variations tidy and predictable. Put them together, and suddenly a single component can stretch to fit diverse brands, layouts, and experiences all without breaking from the system.

Let’s look at a typical “Card” component that is designed with all three of these things in mind such that it can support usage across different brands without breaking a sweat.

Setting up the Basic Card Structure

Say our project calls for a simple card. The card has a toggle-able banner that accepts a text string, a thumbnail image, title, description, and a button.

A card showcasing Red Rocks Park and Amphitheatre with a featured location banner, a vibrant image of a concert, title, description, and a button inviting users to view events.

In this article, all examples are written in Vue, but these principles are not Vue specific. They can be used in any design system that builds from components, even native Web Components.

Our component should end up looking something like this:

<template>
  <article class="system-card">
    <div class="system-card-media">
      <span v-if="bannerText" class="system-card-banner">
        {{ bannerText }}
      </span>
      <img :src="imageUrl" :alt="imageAlt" class="system-card-image" />
    </div>

    <div class="system-card-content">
      <h3 class="system-card-title">{{ title }}</h3>
      <p class="system-card-description">{{ description }}</p>
      <button class="system-card-button">{{ buttonText }}</button>
    </div>
  </article>
</template>

<script setup>
defineOptions({ name: "SystemCard" });

defineProps({
  bannerText: { type: String, default: null },
  imageUrl: { type: String, default: "<https://via.placeholder.com/350x150>" },
  imageAlt: { type: String, default: "Card Image" },
  title: { type: String, default: "Card Title" },
  description: {
    type: String,
    default:
      "This is a description of the card content. It provides more details about the card.",
  },
  buttonText: { type: String, default: "Learn More" },
});
</script>

<style scoped>
.system-card {
  background: #fff;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.system-card-media {
  display: flex;
  flex-direction: column;
}

.system-card-banner {
  background-color: #a3533c;
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  padding: 4px;
  text-align: center;
  width: 100%;
}

.system-card-content {
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* ... other card styling, like .system-card-button */
</style>

The Card is then used like this:

<SystemCard
  bannerText="Featured Location"
  imageUrl="image/of/red-rock.jpg"
  imageAlt="Red Rocks Amphitheater during a night concert"
  title="Red Rocks Park and Amphitheater"
  description="There's No Better Place to See The Stars."
  buttonText="View Events"
/>

Supporting Multiple Brands/Themes with Tokens

At the most basic level, we can modify this card to support different brands or themes through the use of tokens or variables. I will be using CSS Custom Properties (variables) for all examples in this article, but tokens are not limited to just CSS, you can learn more about Design Tokens from the Design Tokens Community Group (which just shipped their first stable version!)

All of our existing markup stays the same, but we need to modify the CSS to use variables for configuration. While we’re at it, we should also look at any duplicate or hard-coded values and convert those to variable as well to maintain clean, reusable code.

Our new style declarations should look like this:

<style scoped>
/* These root variables are likely to be set up in a more global stylesheet */
:root {
  --system-card-bg-color: #ffffff;
  --system-card-accent-bg-color: #a3533c;
  --system-card-accent-text-color: #ffffff;

  --system-card-border-radius: 8px;
  --system-card-padding: 16px;
  --system-card-gap: 8px;
}

.system-card {
  background: var(--system-card-bg-color);
  border-radius: var(--system-card-border-radius);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.system-card-media {
  display: flex;
  flex-direction: column;
}

.system-card-banner {
  background-color: var(--system-card-accent-bg-color);
  color: var(--system-card-accent-text-color);
  font-size: 14px;
  font-weight: 600;
  padding: 4px;
  text-align: center;
  width: 100%;
}

.system-card-content {
  padding: var(--system-card-padding);
  display: flex;
  flex-direction: column;
  gap: var(--system-card-gap);
}
...
</style>

Now that our component is using tokens, we can create a custom class and assign those tokens new values. Simply by swapping from statically assigned values to dynamic tokens; CSS Custom Properties in this case, we have enabled our card component to support different visual themes.

<SystemCard
  class="custom-blue-card"
  bannerText="Featured Location"
  imageUrl="image/of/shedd-aquarium.jpg"
  imageAlt="Underwater tunnel at the Shedd Aquarium"
  title="Shedd Aquarium"
  description="Look Nature in the Eye."
  butonText="Plan Your Visit"
/>
.custom-blue-card {
  --system-card-accent-bg-color: #328198;
  --system-card-accent-text-color: #00070B;
}

Alternatively, we could add a new property on the component that assigns a class with new token definitions if we have a known set of themes we want to support:

<SystemCard
  bannerText="Featured Location"
  imageUrl="image/of/shedd-aquarium.jpg"
  imageAlt="Underwater tunnel at the Shedd Aquarium"
  title="Shedd Aquarium"
  description="Look Nature in the Eye."
  butonText="Plan Your Visit"
  theme="light-blue"
/>
/* These would likely be global overrides in a global stylesheet */
.theme--light-blue {
  --system-card-accent-bg-color: #328198;
  --system-card-accent-text-color: #00070B;
}
Card displaying Shedd Aquarium with a featured location banner, an underwater scene, title 'Shedd Aquarium', description 'Look Nature in the Eye.' and a button 'Plan Your Visit'.

Customizing Content with Composable Slots

A description is great, but what if we want to show a list of details instead? In a situation like this we have two options, either maintain unique code for every different variation of the component, or create composable areas within a single component that engineers can write custom code into, in programming these are often referred to as slots.

Taking our code from before:

<template>
  <article class="system-card">
    <div class="system-card-media">
      <span v-if="bannerText" class="system-card-banner">{{ bannerText }}</span>
      <img :src="imageUrl" :alt="imageAlt" class="system-card-image" />
    </div>
    <div class="system-card-content">
      <h3 class="system-card-title">{{ title }}</h3>
      <p class="system-card-description">{{ description }}</p>
      <button class="system-card-button">{{ buttonText }}</button>
    </div>
  </article>
</template>

We can modify our code to replace the .system-card-description element with a slot, we’ll use the name card-details to identify what we expect the contents of this slot to be.

<template>
  <article class="system-card">
    <div class="system-card-media">
      <span v-if="bannerText" class="system-card-banner">{{ bannerText }}</span>
      <img :src="imageUrl" :alt="imageAlt" class="system-card-image" />
    </div>
    <div class="system-card-content">
      <h3 class="system-card-title">{{ title }}</h3>
      <slot name="card-details" />
      <button class="system-card-button">{{ buttonText }}</button>
    </div>
  </article>
</template>

For the existing cards with a tagline we can simply place the .system-card-description element within the slot to achieve the same result.

<SystemCard
  bannerText="Featured Location"
  imageUrl="image/of/shedd-aquarium.jpg"
  imageAlt="Underwater tunnel at the Shedd Aquarium"
  title="Shedd Aquarium  butonText="Plan Your Visit"
  theme="light-blue"
>
<!-- This is our slot -->
<p class="system-card-description">Look Nature in the Eye.</p>
<!-- This is our slot -->
</SystemCard>
A card component showcasing the Shedd Aquarium with a featured location banner, an underwater scene, and a call-to-action button.

By creating a slot however, we’ve now opened up the possibility for custom content within in. The team can now create a snippet of code to display the list of highlights producing a unique variation on the existing component without completely breaking away from the system.

Component usage could then look like this:

<SystemCard
  bannerText="Featured Location"
  imageUrl="image/of/shedd-aquarium.jpg"
  imageAlt="Underwater tunnel at the Shedd Aquarium"
  title="Shedd Aquarium  butonText="Plan Your Visit"
  theme="light-blue"
>
  <!-- This is our slot -->
  <ul class="highlight-list">
    <li class="highlight-item">
      <p class="highlight-text">Touch Experiences</p>
    </li>
    <li class="highlight-item">
      <p class="highlight-text">Animal Encounters</p>
    </li>
    <li class="highlight-item">
      <p class="highlight-text">Stingray Feedings</p>
    </li>
  </ul>
  <!-- This is our slot -->
</SystemCard>

Producing a result like this:

A card design showcasing the Shedd Aquarium with a featured location banner, an underwater scene, and a list of available experiences including 'Touch Experiences', 'Animal Encounters', and 'Stringray Feedings'. Below, there is a button labeled 'Plan Your Visit'.

A Note on Keeping Things Organized

The concept of completely free and open slots is likely terrifying to a designer or engineer who is focused on maintaining clean and organized code since now we’re allowing people to add whatever custom work they want to an area.

To avoid this we can provide “ready-made” child components that we would prefer teams use in these areas, whether through a predetermined list of parts that we know consumers will want, or by paying attention to and adopting repeated usage patterns.

In our examples so far we know we want to support system-card-description and a new system-card-list as options for this space; we can create those as smaller components or “partials.” Engineering and design teams can then use those formally adopted and verified options when they fit their needs, and they maintain all of the benefits of using system components over needing to create custom solutions.

Using these partials might look like this:

<SystemCard
  bannerText="Featured Location"
  imageUrl="image/of/shedd-aquarium.jpg"
  imageAlt="Underwater tunnel at the Shedd Aquarium"
  title="Shedd Aquarium  butonText="Plan Your Visit"
  theme="light-blue"
>
  <!-- This is our slot -->
  <SystemCardDescription text="Look Nature in the Eye.">
  <!-- This is our slot -->
</SystemCard>

or:

<SystemCard
  bannerText="Featured Location"
  imageUrl="image/of/shedd-aquarium.jpg"
  imageAlt="Underwater tunnel at the Shedd Aquarium"
  title="Shedd Aquarium  butonText="Plan Your Visit"
  theme="light-blue"
>
  <!-- This is our slot -->
  <SystemCardList items="[array of items]">
  <!-- This is our slot -->
</SystemCard>

Extending Configuration and Composability for Further Customization

Using slots and partials for configuration and composition is not limited to single areas within a component either, once you start thinking in this model you can create incredibly flexible components that can support a vast array of different styles and layouts.

Examining our card component through the lenses of composition and configuration we can create a layout like this.

An aerial view of the Adler Planetarium showcasing its unique dome structure and surrounding landscape, featuring a prominent banner reading 'Escape to the Stars'. Below, there is a section displaying the title 'Adler Planetarium' and a list of upcoming events.

The first thing we need to do is identify what pieces are configurable and what pieces are composable. Configurable elements are typically controlled through attributes, where composable sections are typically slots.

In our case the location of the .card-media__banner can be configurable to either the top or bottom of the image.

As for composition, we’ve taken the entire system-card-content area and turned it into a slot, allowing users of the component to build out whatever layout meets their needs. In this case we’re putting the button first, followed by the title, a list of details, and social links..

The component code now looks like this:

<template>
  <article class="system-card">
    <CardMedia
      :bannerText="bannerText"
      :bannerLocation="bannerLocation"
      :imageUrl="imageUrl"
      :imageAlt="imageAlt"
    />
    <div class="system-card-content">
      <slot name="card-content" />
    </div>
  </article>
</template>

<script setup>
import CardMedia from "./partials/CardMedia.vue";

defineOptions({ name: "SystemCard" });

defineProps({
  bannerText: { type: String, default: null },
  bannerLocation: { type: String, default: "top" },
  imageUrl: { type: String, default: "<https://via.placeholder.com/350x150>" },
  imageAlt: { type: String, default: "Card Image" },
  title: { type: String, default: "Card Title" },
  description: {
    type: String,
    default:
      "This is a description of the card content. It provides more details about the card.",
  },
  buttonText: { type: String, default: "Learn More" },
});
</script>

and in use:

<SystemCard
  bannerText="Escape to the Stars"
  bannerLocation="bottom"
  imageUrl="image/of/adler-planetarium.jpg"
  imageAlt="Aerial view or Adler Planetarium"
>
  <SystemButton type="filled" text="Join Us" iconEnd="arrow-right" />
  <SystemCardTitle text="Adler Planetarium" />
  <SystemCardList
    title="Upcoming Events and Shows"
    items="[array of items]">
    <SystemCardSocials
      facebook="link.to.social"
      twitter="link.to.social"
      youtube="link.to.social"
      instagram="link.to.social"
    />
  </SystemCardList>
</SystemCard>

We’ve moved the banner and image to a new partial we’re importing called CardMedia that partial is then passed the prop bannerLocation and determines whether the banner should appear on the top of bottom of the image. Now, because our entire content area is a slot we’ve added multiple elements as children of the SystemCard; we’ve got a SystemButton, SystemCardTitle, SystemCardSocial, and our SystemCardList from before, in a new layout that we have defined ourselves all while continuing to use the design system without breaking.

Our final result is a highly adaptable card that supports all of our examples through the use of configuration through props and configuration through custom slots.

Three card components showcasing different featured locations with images, titles, descriptions, and action buttons.

Turning Principles into Practice

Supporting multiple brands and use‑cases doesn’t have to mean duplicating components or maintaining endless forks of code. By grounding your system in tokens, composition, and configuration, you can keep one core component flexible enough to handle divergent needs.

  • Tokens: Centralize design decisions like color, spacing, and typography so brand shifts are a matter of swapping variables or themes, not rewriting CSS.
  • Composition (slots/partials): Create structured areas where teams can plug in approved variations, reducing the need for custom one‑offs while still allowing an escape hatch when needed.
  • Configuration (props/attributes): Expose common and repeated options for styles, layouts, and behavior so components adapt without breaking consistency.

The payoff is huge: fewer bespoke components to maintain, faster delivery across brands, and a system that scales without losing cohesion. Instead of fighting divergence, you’re designing for it, and that’s how systems stay resilient in the real world.

]]>
https://frontendmasters.com/blog/exploring-multi-brand-systems-with-tokens-and-composability/feed/ 0 8041
The Vue Ecosystem in 2024 https://frontendmasters.com/blog/the-vue-ecosystem-in-2024/ https://frontendmasters.com/blog/the-vue-ecosystem-in-2024/#comments Thu, 02 May 2024 16:49:50 +0000 https://frontendmasters.com/blog/?p=1849 Since its inception in February 2014, Vue has become one of the top JavaScript frameworks for building and maintaining web apps. It is a popular pick for many developers because of its philosophy of creating an approachable, performant, and versatile approach to building scalable apps.

Once you’ve chosen Vue to build your app, it’s important to consider what tools and libraries can help you ship features faster to users. While a lot has changed over the years, the addition of new features like the Composition API has introduced lots of exciting new changes to the ecosystem.  Let’s look at some of the popular tools and libraries used by the community today.

Getting Started with Vue

There are three primary approaches to scaffolding your Vue app:

  1. Content Delivery Network (CDN)
  2. Build Tools
  3. Using a meta-framework like Nuxt

CDN

While most JavaScript frameworks require a build toolchain to work, one of the great things about Vue is that it can be added directly on a page. No build tools necessary!

<!-- unpkg -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<!-- jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.25/dist/vue.global.min.js"></script>

Or if you’re already using ES modules, you can also use it as follows:

<script type="module">
  import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
  // or 'https://esm.sh/vue'

  createApp({
    setup() {
      const message = ref('Hello Vue!')
      return {
        message
      }
    }
  }).mount('#app')
</script>

To learn more, check out the official docs.

Build Tools

When your app is ready for build tools (e.g., Vite, Rollup, webpack) and/or you want to use Single-File Components (*.vue), the best way is to use Vite for Vue 3 projects. Why Vite? It leverages both latest advancements in the ecosystem: the availability of native ES modules in the browser, and the rise of JavaScript tools written in compile-to-native languages.

Fun fact, Vite was written by Evan You (founder of Vue), so naturally there’s a way to scaffold a Vue project with it!

npm create vue@latest

To learn more about creating a Vue application with Vite, check out the official docs.

Meta-frameworks

For those interested in taking Vue beyond client-side rendering to other realms such as static-site generation (SSG), server-side rendering (SSR), and other rendering methods, you’ll want to start your project with scaffolding from popular meta-frameworks such as Nuxt.

npx nuxi@latest init <project-name>

To learn more, check out the official docs for getting started with Nuxt.

That said, here are two other options I wanted to give a shoutout to:

Meta Framework

VitePress

A fantastic static site generator that uses Markdown to create beautiful docs in minutes (while powered by Vue theming for easy customization).

Meta Framework

Quasar

A cross-platform Vue meta-framework that comes with a lot things out of the box like Material components, various build modes, and much more.

Routing

When it comes to your routing solution in Vue, it’s a straightforward choice because Vue has an official library for routing, but a meta framework can help as well.

Meta Framework

Vue Router

This is the official router for Vue. Expressive, configurable and convenient routing.

Meta Framework

Nuxt

For those who love file-based routing, you’ll get that automatically in conjunction with Vue Router when you use Nuxt with no additional configuration needed.

If you’re interested in following or contributing to an up-and-coming extension on Vue Router that allows you to do file-based routing with TS support, be sure to keep an eye on unplugin-vue-router!

State Management

Similar to routing, Vue makes it easy for you by providing an official library for state management, but since state management is more particular to the requirements of an app and how a team approaches it, here are some popular options in the community.

State Management

Pinia

Pinia, formerly known as Vuex, is the official state management library for Vue. On top of being TypeScript-friendly, it takes a huge leap forward from the traditional redux pattern by eliminating the need for explicitly defining mutations. In other words, a lot less code while still being declarative!

State Management

XState

For those familiar with state machines, you’ll want to check out XState as an option for managing state within your app. As a bonus, a fantastic team backs it and has been a consistent favorite within the community.

State Management

TanStack Query

One of the major challenges that developers face with state management is around granular state management within the context of async calls. TanStack Query is a popular pick in the community for helping to manage this.

State Management

Composables

With the addition of the Composition API, Vue makes it easier than ever for developers to create their own state management solutions with composables. While this can be useful in certain scenarios, it’s recommended to use a standard like Pinia to ensure consistent patterns across teams.

Component Libraries

Selecting the right component library depends on your app’s requirements. Here are some popular libraries that aim to provide accessible components and are backed by good English documentation.

Component Library

PrimeVue

A powerful and flexible component library that contains styled and unstyled components to make it easier to cater to your app’s needs.

Component Library

Radix Vue

Unstyled and accessible components for building high‑quality design systems and web apps in Vue

Component Library

Quasar Components

UI components that follow Material Guidelines for those using Quasar.

Component Library

Vuetify

A longstanding favorite in the Vue community for Material components that has great docs.

Component Library

NuxtLabs UI

Fully styled and customizable components for those using Nuxt

Honorable Mentions

Component Library

Buefy

For those who love Bulma, this is in active development for Vue 3.

Component Library

Element Plus

A Vue 3 UI Library made by Element team with a strong Chinese and English community.

Component Library

BootstrapVue

For those who love Bootstrap, it’s not fully ready for Vue 3 at the time of publication, but keep an eye on this library.

Component Library

Vuestic UI — Vue 3 UI framework

A free and open source UI Library for Vue 3

Testing

When it comes to testing your app, here’s a quick overview of what the community is using.

Unit

Unit Testing

Vitest

No contest here. With most Vue projects being Vite based, the performance gain here over previous unit testing libraries like Jest is hard to ignore.

Component

There are three primary options for component testing depending on your use case:

Component Testing

Vitest

Vitest is great for components or composables that render headlessly.

Component Testing

Vue Testing Library

Vue Testing Library is light-weight solution for testing Vue components that provides light utility functions on top of @vue/test-utils.

Component Testing

Vue Test Utils

Vue Test Utils is another option for testing components and DOM.

Component Testing

Cypress Component Testing

Cypress is great for components whose expected behavior depends on properly rendering styles or triggering native DOM events.

E2E

While other tests are great, one of my mentors likes to say that there are only two tests that matter for any code base. 

  1. Can the user login?
  2. Can the user pay us?

Everything else is optional.  

And that’s where end-to-end (E2E) tests come into play. There are two primary candidates to choose from when selecting your E2E testing framework:

End to End Testing

Cypress

Cypress has been a longtime favorite of the community with its intuitive API and ability to run and debug tests directly in the browser.

End to End Testing

Playwright

Playwright has become another popular choice amongst the community as well for its ability to test across any browser or platform using a singular API.

For additional reading about testing in Vue, check out the official docs.

Mobile Development

Mobile Framework

Capacitor

You might be familiar with Ionic, a popular open-source UI toolkit for building mobile apps with web technologies. Well, Capacitor is the foundation that Ionic is built on top of which enables you to build the actual app.

It’s also backed by a team that have been great partners to the Vue community. Definitely worth checking out if you want to build a mobile app with Vue.

You can learn more at their official docs for Capacitor + Vue.

Mobile Framework

NativeScript

If you’re looking for a React Native equivalent in Vue, while there is no official support from the core team, NativeScript is a solution for building true native mobile apps with JavaScript.

Note: It’s currently in release candidate (RC) and not officially production ready, but it has been a past favorite for Vue 2 apps, so it’s included as an alternative to Capacitor.

To learn more or follow along, check out the official repo for NativeScript

Bonus Libraries

These libraries don’t have a distinct category of their own, but I wanted to give a special shoutout to the following libraries:

Bonus

VueUse

When it comes to utility libraries, VueUse gets the MVP award for providing an incredible assortment of useful abstractions for common functionality such as copying to clipboard, dark / light mode, local storage, and more.

Bonus

Petite-Vue

For those who need only a subset of Vue features and just want to sprinkle some interactivity onto a page, you definitely want to look into petite-vue, which is only ~6kb! 

Bonus

FormKit

Let’s face it. Forms are a big part of most web apps, whether you like it or not. FormKit is a library that aims to make the process easier by providing standards for things like form structure, generation, validation, and more.

Bonus

TresJS

TresJS is a library that aims to make building 3D experiences with Three.js and Vue a lot easier.

Looking Ahead

While I’ve highlighted some popular solutions in the ecosystem, I want to emphasize that at the end of the day, what matters most is choosing the right solution for your team and app. If the solution you and/or your team are using didn’t make it on this list, that doesn’t make it any less valid.

Finally, the reality is that the landscape for tooling and libraries is constantly changing. While it may seem like specific problems have “already been solved,” I assure you there is still much room for new ideas and improvements. The community is still ripe for contributions, and I hope to recommend something you build someday!

]]>
https://frontendmasters.com/blog/the-vue-ecosystem-in-2024/feed/ 2 1849