How to Create Reusable CSS Components with Bit

ni
nitsan77011 months ago

In this post, we focus on creating reusable CSS components using Bit. Bit allows us to encapsulate styling rules into independent components that can be shared and managed across different projects.

These CSS components can be used in various environments, regardless of the frameworks in use - React, Angular, Vue, or others. The compatibility of Bit with diverse styling solutions enhances its flexibility, and we will use SCSS as a representative example in this context.

The creation of independent components promotes reusability and consistency in your design system and UI library. Beyond that, Bit also allows us to compose these components together, enabling more complex structures while maintaining modularity.

One of the notable benefits of using Bit is its support for incremental updates. This allows for iterative improvements of components without affecting existing project structures. In the following sections, we will walk through the process of creating these components and demonstrate how they can be composed and updated incrementally.

Our Demo

Our demo includes 3 SCSS components:

  1. variables/colors - defines color schemes for consistent color usage across your applications.
  2. layout/spacing - includes a function for calculating spacing and our base spacing variable.
  3. layout/breakpoints - defines different screen sizes and includes a series of mixins for different media queries.

These 3 SCSS components compose the blocks/container component, which defines a container with a background color, padding, and media queries for different screen sizes.

Choosing an Env for SCSS Components

This blog uses SCSS/CSS component templates, provided by the HTML env. These templates provide the basic structure for creating SCSS/CSS components, and are configured to use the HTML env, which is a framework-agnostic environment for frontend components.

"teambit.generator/generator": {
    "envs": ["teambit.html/html-env", "teambit.react/react-env"]
  },
CopiedCopy

We also added the react-env so we can use it later to generate a React component.

Component #1: 'variables/colors'

First, let's create a SCSS component variables/colors that defines color schemes for consistent color usage across your applications:

$bit
Copiedcopy

The implementaion of this component is very straightforward. We define two variables, primary and secondary, and export them as a map:

/* @filename: colors.module.scss */
$primary-color: #007bff;
$secondary-color: #6c757d;

:export {
  primary: $primary-color;
  secondary: $secondary-color;
}
CopiedCopy

Much like other Bit components, CSS components require a main file or entry file, to expose their API. In this case, the main file will export the color constants as follows:

/* @filename: index.ts */

import colors from './_colors.module.scss';

export const { primary, secondary } = colors;
CopiedCopy

Component #2: 'layout/spacing'

Next, we'll create a layout/spacing component that includes a function for calculating spacing and our base spacing variable:

$bit
Copiedcopy

In this example, we define a base spacing variable and a spacing function that multiplies the base spacing by a given multiplier. We then export the base spacing variable and the spacing function as a map:

/* @filename: spacing.module.scss */

$base-spacing: 10px;

@function spacing($multiplier) {
  @return $base-spacing \* $multiplier;
}

:export {
  base: $base-spacing;
}
CopiedCopy

Component #3: 'layout/breakpoints'

Then, we create a layout/breakpoints component that defines different screen sizes and includes a series of mixins for different media queries:

$bit
Copiedcopy
/* @filename: breakpoints.module.scss */

// breakpoint sizes
$xs: 360px;
$sm: 480px;
$md: 768px;
$l: 920px;
$lg: 1200px;
$xl: 1440px;
$xxl: 1920px;

// exporting sizes to be used via js
:export {
  xs: $xs;
  sm: $sm;
  md: $md;
  l: $l;
  lg: $lg;
  xl: $xl;
  xxl: $xxl;
}

// generic media query mixin. do not use directly
@mixin media($breakpoint) {
  @media only screen and (max-width: $breakpoint) {
    @content;
  }
}

// media queries for each size to be used directly
@mixin media-xs {
  @include media($xs) {
    @content;
  }
}
@mixin media-sm {
  @include media($sm) {
    @content;
  }
}
// (continue for all sizes)
CopiedCopy

Composing SCSS Components Together

In this example, we're creating an independent component called blocks/container that uses the SCSS components we created in the previous section:

$bit
Copiedcopy

This SCSS file demonstrates how to utilize the SCSS functions, mixins, and variables defined in separate, independent SCSS components. By sharing and reusing these components across the entire organization, you can ensure design consistency and reduce code duplication.

// container.module.scss
@use '@teambit/your-organization.colors/colors.module.scss' as colors;
@use '@teambit/your-organization.spacing/spacing.module.scss' as spacing;
@use '@teambit/your-organization.breakpoints/breakpoints.module.scss' as
  breakpoints;

.container {
  background-color: colors.$primary-color;
  padding: spacing.spacing(2);

  @include breakpoints.media-md {
    padding: spacing.spacing(4);
  }

  @include breakpoints.media-lg {
    padding: spacing.spacing(6);
  }
}
CopiedCopy

Bit maintains the independence of each component, while keeping track of the dependencies between them. This allows us to compose these components together to create more complex structures, while maintaining modularity.

Utilizing SCSS Components in Different Environments

Next, let's dive into how we can utilize the container style in various environments, such as React, Angular, and Vue components managed by Bit. Bit empowers us to create independent, reusable components that can be seamlessly integrated into different projects and frameworks.

Let's start by examining how we can utilize the container style in a React Bit component

$bit
Copiedcopy
// React.tsx
import { ReactNode } from 'react';
import { container } from '@learnbit/css.blocks.container';

export type ReactProps = {
  /**
   * a node to be rendered in the special component.
   */
  children?: ReactNode,
};

export function ReactExample({ children }: ReactProps) {
  return <div className={container}>{children}</div>;
}
CopiedCopy

In the example above, we import the container.module.scss file, assigning the container class from the imported styles object to our component's div. This applies the styles defined in the container class to our React component.

Now, let's delve into how we can utilize the container style in an Angular Bit component. We'll skip the component creation step, as it's identical to the React component creation process (you'll have to add the teambit.angular/angular env to the generator's envs array in the workspace.jsonc file, though).

<!-- my-component.component.html -->

<div class="container">Content goes here</div>
CopiedCopy
/* my-component.component.scss */
@use '@learnbit/css.layout.container/container.module.scss' as container;

.container {
  @include container.container;
}
CopiedCopy

With Angular, we can directly employ the container class in the component's HTML template. To apply the styles from the container.module.scss file, we import it as container and then include the container class using the @include directive in the component's SCSS file.

Finally, let's explore how we can utilize the container style in a Vue Bit component:

<!-- MyComponent.vue -->

<template>
  <div class="container">Content goes here</div>
</template>

<style scoped lang="scss">
  @use '@learnbit/css.layout.container/container.module.scss' as container;

  .container {
    @include container.container;
  }
</style>
CopiedCopy

In Vue, we define the container class directly in the component's template. To apply the styles from the container.module.scss file, we import it as container and include the container class using the @include directive in the component's scoped SCSS block.

By leveraging Bit's component-based approach, along with the shared styles, we can maintain consistency across various projects and frameworks, while ensuring that each team retains their autonomy.

Incremental Builds and Automatic Tagging with Bit

The next essential feature of Bit we will look at is its ability to manage incremental builds and automatic tagging. This is one of the key capabilities that allow Bit to maintain consistency and modularity across multiple projects and frameworks.

Consider a scenario where we make an update to the spacing style. As soon as we tag this change, Bit is smart enough to recognize that any component dependent on this spacing style, like our container, needs to be rebuilt and versioned as well.

This cascading effect even extends to the framework-specific components that are utilizing the container style. This means, if we have a React, Angular, or Vue component using the container style, Bit will automatically rebuild, retest, and version these components as well.

Let's see this in action:

  1. We make a change to our spacing component.
// spacing.module.scss
$base-spacing: 8px;

@function spacing($multiplier) {
  @return $base-spacing \* $multiplier;
}

:export {
  base: $base-spacing;
}
CopiedCopy
  1. We tag all components that have been modified.
$bit
Copiedcopy
  1. Bit identifies that spacing has changed and that container is dependent on spacing.
  2. Bit automatically tags a new version of container.
  3. Bit also identifies that our React, Angular, and Vue components are dependent on container.
  4. Bit then tags new versions of these components as well.
  1. This automatic tagging ensures that all dependent components are kept up-to-date with the latest changes, maintaining consistency across all projects and frameworks. It also allows teams to work independently, knowing that their components will remain in sync with any updates to the shared styles.

This kind of intelligent dependency management is what makes Bit such a powerful tool for building and managing reusable components across various projects and frameworks.

Conclusion

Throughout this blog post, we've walked you through the process of creating, composing, and leveraging reusable CSS components with Bit. By encapsulating our styling rules into distinct, standalone units, we've achieved significant strides towards a consistent, maintainable design system.

Bit's capacity to support a broad range of frameworks allows these components to be integrated seamlessly into diverse projects. Whether it's React, Angular, Vue, or others, the use of these components remains consistent and straightforward.

Furthermore, Bit's intelligent dependency management and automatic tagging system enable incremental updates without affecting the overall project structure. This feature ensures that all dependent components are automatically versioned and updated when a change is made, thus reducing manual overhead and potential inconsistencies.

In essence, Bit empowers teams with true autonomy, while maintaining a synchronized, up-to-date state across all projects and frameworks. This balance between independence and consistency is a key enabler of efficient collaboration and code reuse.

By incorporating Bit into your development workflow, you're investing in a more efficient, consistent, and manageable codebase. Whether you're building a complex design system or a small UI library, Bit's reusable CSS components will undoubtedly prove to be a valuable asset.