bootstrap-vue VS Tailwind CSS

Compare bootstrap-vue vs Tailwind CSS and see what are their differences.

bootstrap-vue

BootstrapVue provides one of the most comprehensive implementations of Bootstrap v4 for Vue.js. With extensive and automated WAI-ARIA accessibility markup. (by bootstrap-vue)
InfluxDB - Purpose built for real-time analytics at any scale.
InfluxDB Platform is powered by columnar analytics, optimized for cost-efficient storage, and built with open data standards.
www.influxdata.com
featured
SaaSHub - Software Alternatives and Reviews
SaaSHub helps you find the best software and product alternatives
www.saashub.com
featured
bootstrap-vue Tailwind CSS
44 1,362
14,498 81,714
0.1% 1.2%
0.9 9.6
2 months ago 7 days ago
JavaScript TypeScript
MIT License MIT License
The number of mentions indicates the total number of mentions that we've tracked plus the number of user suggested alternatives.
Stars - the number of stars that a project has on GitHub. Growth - month over month growth in stars.
Activity is a relative number indicating how actively a project is being developed. Recent commits have higher weight than older ones.
For example, an activity of 9.0 indicates that a project is amongst the top 10% of the most actively developed projects that we are tracking.

bootstrap-vue

Posts with mentions or reviews of bootstrap-vue. We have used some of these posts to build our list of alternatives and similar projects. The last one was on 2024-06-11.
  • Awesome | Top 9 UI Frameworks for Vue.js
    7 projects | dev.to | 11 Jun 2024
    BootstrapVue brings the power of Bootstrap to Vue.js. It includes a comprehensive implementation of Bootstrap 4 components and grid system, along with extensive support for custom themes.
  • 10 UI Libraries You Should Explore for Your Next Vue.js Project
    7 projects | dev.to | 29 Oct 2023
    4. Bootstrap Vue Bootstrap Vue combines the power of Bootstrap, a popular CSS framework, with Vue.js. It provides a wide range of components and styling options. Check out the Bootstrap Vue website to learn more.
  • [AskJS] UI libraries either backed by enterprises/quick fix of issues/has almost no issues with default styling that is customizable
    3 projects | /r/javascript | 3 May 2023
    Vue (BootstrapVue)
  • What is the story with BootstrapVue now?
    2 projects | /r/vuejs | 1 Mar 2023
    You say that Afaik is the creator? I didn't see him listed as a major contributor on github: https://github.com/bootstrap-vue/bootstrap-vue/graphs/contributors . Do you happen to have his Github profile link?
  • Vue 3 UI Framework recommendations?
    2 projects | /r/webdev | 25 Jan 2023
  • Vue or React? Which one is easier to pick up?
    4 projects | /r/learnprogramming | 18 Jan 2023
    For me personally one of the dealbrakers was bootstrap-vue still being stuck with Vue v2 / Bootsrap v4 to this day. react-bootstrap supports Bootstrap v5 since 2021 october.
  • Top UI libraries for Vue JS in 2023
    4 projects | dev.to | 3 Jan 2023
    Bootstrap-Vue: A UI library that provides a range of components based on the popular Bootstrap framework, including forms, buttons, and navbars.
  • Fragen bezüglich Flask, Zahlungsgateway, Design und JavaScript
    2 projects | /r/de_EDV | 4 Dec 2022
  • Noob question: What do you use to build your front ends?
    2 projects | /r/laravel | 12 Jul 2022
    BootstrapVue is the bundle: https://bootstrap-vue.org/
  • Don't be that open-source user, don't be me
    6 projects | news.ycombinator.com | 19 Jun 2022
    Yes. Please have the courtesy to feedback with a roadmap or prio of the issue. Especially for popular issues.

    Asking, politely, for this should not label you as entitled freeloader. It is important input to make an informed decision wether one should just wait for the fix, workaround it, contribute a PR yourself, fork the component or drop it and consider alternatives.

    One has to be careful with estimates though so they don’t become false promises. All respect to these maintainers but if I have to give one concrete example, consider following issue in a very popular Vue component, https://github.com/bootstrap-vue/bootstrap-vue/issues/5196 creating a upgrade deadlock for almost the entire Vuejs community. It’s the type of dependency that get so entrenched in everybody’s application that upgrading or moving away from it becomes very expensive and requires long term planning. As such, hundreds of comments there asking for estimates and also dozens of heavy names offering help in forms of PRs, forks or donations, all on a very polite level, but the maintainers kept promising it will be done “very soon” for almost 2 years straight. It appears the last months the war has been adding more obstacles so all respect for that, but even before the roadmap was hopelessly unpredictable.

    I get it, as a volunteer other things in life often have higher prio, estimates tend to be optimistic and you might want to work on things in no particular order at all. What’s important is to be transparent, polite and communicate.

Tailwind CSS

Posts with mentions or reviews of Tailwind CSS. We have used some of these posts to build our list of alternatives and similar projects. The last one was on 2024-09-12.
  • Can You Clear This Challenge Site Designed for Engineers?
    7 projects | dev.to | 12 Sep 2024
    Tailwind Convenient
  • Rethinking CSS in JS
    32 projects | dev.to | 12 Sep 2024
    tag: Injection into the of the
  • Inline style: Injected inline for each HTML element
  • Styles apply method
    • className: Specify className as a string
    • styled component: Create and use Styled Components
    • css prop: Generate className from the css prop in jsx
  • Atomic CSS: Create separate classes for each CSS property
  • 6.2 CSS Processors

    There might be a way to solve this by extending CSS rather than using a JavaScript-centric approach. (image source)

    CSS Processors

    CSS preprocessors use their own syntax to extend CSS for writing code, which is then compiled into regular CSS.
    Examples of CSS preprocessors include Sass, Less, and Stylus.

    CSS postprocessors use standard CSS syntax as-is and transform it through a plugin system.
    PostCSS is an example of a CSS postprocessor, and Lightning CSS also has postprocessing capabilities.

    Here, the nesting and mixin features are particularly worth discussing.
    I will explain based on SCSS.

    The nesting feature allows you to write CSS code in a structure similar to HTML.

    .nav {
      background-color: #f0f0f0;
      ul {
        list-style: none;
        li {
          display: inline-block;
          a {
            color: #333;
            &:hover {
              color: #007bff;
            }
          }
        }
      }
    }
    
    Enter fullscreen mode Exit fullscreen mode

    You can use the & symbol to reference the parent selector. This is useful when creating pseudo-classes or modifier classes.

    .button {
      background-color: #007bff;
    
      &:hover { background-color: #0056b3; }
      &--large { font-size: 18px; }
      &--small { font-size: 14px; }
    }
    
    Enter fullscreen mode Exit fullscreen mode

    Related properties and media queries can also be nested, improving the structure of the code.

    .element {
      font: {
        family: Arial;
        size: 16px;
      }
    
      @media (min-width: 768px) {
        font-size: 18px;
      }
    }
    
    Enter fullscreen mode Exit fullscreen mode

    Mixins are reusable style blocks that allow you to create styles in advance or vary them with parameters.

    @mixin center-text {
      text-align: center;
      vertical-align: middle;
    }
    
    @mixin border-radius($radius: 3px) {
      border-radius: $radius;
    }
    
    .header {
      @include center-text;
      @include border-radius(5px);
    }
    
    .button {
      @include border-radius(); // Use default value
    }
    
    Enter fullscreen mode Exit fullscreen mode

    You can also apply styles using control logic or apply additional styles using the @content directive.

    @mixin media-query($breakpoint) {
      @if $breakpoint == tablet {
        @media (min-width: 768px) and (max-width: 1023px) {
          @content;
        }
      } @else if $breakpoint == desktop {
        @media (min-width: 1024px) {
          @content;
        }
      } @else {
        @warn "Unknown breakpoint: #{$breakpoint}";
      }
    }
    
    .responsive-element {
      font-size: 16px;
    
      @include media-query(tablet) {
        font-size: 18px;
      }
    
      @include media-query(desktop) {
        font-size: 20px;
      }
    }
    
    Enter fullscreen mode Exit fullscreen mode

    6.3 Semantic CSS and Atomic CSS

    The biggest difference between Semantic CSS and Atomic CSS is likely whether they prioritize visual appearance or meaning.
    When prioritizing visual appearance, you might use something like .text-red { color: red; }, while prioritizing meaning would lead to something like .error { color: red; }.

    Due to these characteristics, the methods for achieving goals also differ.
    For example, what if you needed to create various designs with the same content, like in CSS Zen Garden?
    The visual approach would keep the CSS fixed but achieve the goal by modifying the HTML.
    The semantic approach would keep the HTML fixed but achieve the goal by modifying the CSS.

    CSS zen garden

    Maximizing the visual approach minimizes CSS file size and reduces coupling by reusing small, single-purpose classes.
    This is precisely Atomic CSS.

    CSS file size

    Atomic CSS is a functional CSS approach that composes styles by combining small, single-purpose classes.

    .m-0 { margin: 0; }
    .p-1 { padding: 1rem; }
    .text-center { text-align: center; }
    
    Enter fullscreen mode Exit fullscreen mode
     class="m-0 p-1 text-center">
      Hello, World!
    
    Enter fullscreen mode Exit fullscreen mode

    Maximizing the semantic approach improves code readability and increases CSS cohesion. It also facilitates better separation of content(HTML) and formatting(CSS).
    The class serves as an identifier for grouped elements and is also a W3C recommendation because it emphasizes the separation between HTML and CSS.

    BEM is one of the most popular semantic CSS methodologies. (image source)
    BEM

    BEM(Block Element Modifier) structures class names of HTML elements by naming them as Block, Element, and Modifier.

    • Block: Independent component
    • Element: Part of a Block
    • Modifier: Variation of Block or Element
    /* Structure */
    .block {}
    .block__element {}
    .block--modifier {}
    
    /* Example */
    .button {}
    .button__icon {}
    .button--large {}
    
    Enter fullscreen mode Exit fullscreen mode
     class="button button--large">
       class="button__icon">📁
      Open File
    
    
    Enter fullscreen mode Exit fullscreen mode

    BEM makes it possible to understand the HTML structure through class names alone.

    6.4 CSS Methodologies

    CSS methodologies are not limited to just Atomic CSS and BEM.
    There are many diverse CSS methodologies, and I will compare them to find common elements among these methodologies.

    CSS Methodologies

    Here are some famous CSS methodologies:

    1. OOCSS(Object Oriented CSS): A methodology that separates CSS into reusable 'objects', managing structure and skin independently.
    2. SMACSS(Scalable and Modular Architecture for CSS): A methodology that categorizes CSS into five categories: Base, Layout, Module, State, and Theme.
    3. ITCSS(Inverted Triangle CSS): An architecture that organizes CSS hierarchically based on specificity, explicitness, and reach.
    4. SUITCSS: A component-based CSS methodology with structured class names and clear relationships.
    5. CUBE CSS: A methodology that provides flexible and scalable styling by utilizing the inherent characteristics of CSS using the concepts of Composition, Utility, Block, and Exception.

    6.4.1 OOCSS (Object Oriented CSS)

    OOCSS is a methodology for writing CSS in an object-oriented way. Its main principles are:

    • Visual focus: Separation of structure and skin
      • Structure: Defines layout-related properties such as size, position, and margins of elements.
      • Skin: Defines visual styles such as colors, fonts, and shadows.
    • Information focus: Separation of container and content
      • Container: The parent element that wraps other elements, mainly responsible for layout.
      • Content: The element containing the actual content, which should maintain consistent styling regardless of its location.

    Example:

    /* Structure */
    .button {
      display: inline-block;
      padding: 5px 10px;
      border-radius: 5px;
    }
    
    /* Skin */
    .button-primary {
      background-color: blue;
      color: white;
    }
    
    /* Container */
    .sidebar {
      width: 300px;
      float: left;
    }
    
    /* Contents */
    .widget {
      margin-bottom: 20px;
      padding: 15px;
      background-color: #f0f0f0;
    }
    
    Enter fullscreen mode Exit fullscreen mode

    OOCSS can increase reusability and reduce CSS file size.

    6.4.2 SMACSS (Scalable and Modular Architecture for CSS)

    SMACSS is a methodology that divides CSS into five categories:

    1. Base: This category defines basic styles, including default styles for element selectors and CSS resets.
    2. Layout: This category defines the main structure and grid system of the page, responsible for layouts such as headers, footers, and sidebars.
    3. Module: This category defines reusable independent components, including UI elements such as buttons, cards, and navigation.
    4. State: This category represents specific states of modules or layouts, defining state changes such as active, inactive, and hidden.
    5. Theme: This category defines the overall look and feel, including styles related to themes such as color schemes and typography.

    Example:

    /* Base */
    body, p, h1, h2, h3 { margin: 0; padding: 0; }
    
    /* Layout */
    .l-header { ... }
    .l-sidebar { ... }
    
    /* Module */
    .btn { ... }
    .btn-primary { ... }
    
    /* State */
    .is-hidden { display: none; }
    
    /* Theme */
    .theme-dark .btn { background-color: #333; }
    
    Enter fullscreen mode Exit fullscreen mode

    SMACSS allows for systematic management of CSS structure.

    6.4.3 ITCSS (Inverted Triangle CSS)

    ITCSS is a methodology that organizes CSS hierarchically according to specificity, explicitness, and reach. (image source)

    ITCSS

    The main layers are as follows:

    1. Settings: This layer includes basic project settings such as global variables, color definitions, and font settings.
    2. Tools: This layer defines globally used tools such as mixins and functions, with no actual CSS output.
    3. Generic: This layer defines the most basic and high-level styles, such as resets and normalization.
    4. Elements: This layer defines the basic styles of HTML elements, using only element selectors without classes.
    5. Objects: This layer defines structural and skeletal styles in the OOCSS approach, including reusable design patterns.
    6. Components: This layer defines styles for specific and complete UI components.
    7. Utilities: This layer includes utility classes that forcefully apply specific styles, being the most specific and highest priority.

    This methodology minimizes specificity conflicts in CSS and improves maintainability.

    6.4.4 SUITCSS

    SUITCSS is a component-based CSS methodology with structured class names and clear relationships:

    • Component: The base component class like ComponentName
    • Modifier: Changes the style of the component like .ComponentName--modifierName
    • Descendent: Part of the component like .ComponentName-descendentName
    • Utility: Single-purpose styling like .u-utilityName
    /* Component */
    .Button { ... }
    .Button-icon { ... }
    
    /* Modifier */
    .Button--large { ... }
    
    /* Utility */
    .u-textCenter { text-align: center; }
    
    Enter fullscreen mode Exit fullscreen mode
     class="Button Button--large">
       class="Button-icon">
      Submit
    
     class="u-textCenter">
      Centered text
    
    Enter fullscreen mode Exit fullscreen mode

    6.4.5 CUBE CSS

    CUBE CSS stands for Composition, Utility, Block, Exception and encompasses the following key concepts:

    • Composition: Defines layout patterns
    • Utility: Defines small units of styles
    • Block: Defines reusable components
    • Exception: Handles exceptions in specific situations

    It maximize the use of CSS Cascade and others.

    Example:

    /* Composition */
    .cluster {
      display: flex;
      flex-wrap: wrap;
      gap: 1rem;
    }
    
    /* Utility */
    .center {
      text-align: center;
    }
    
    /* Block */
    .card {
      background: white;
      padding: 1rem;
      border-radius: 0.25rem;
    }
    
    /* Exception */
    .card[data-state="active"] {
      border: 2px solid blue;
    }
    
    Enter fullscreen mode Exit fullscreen mode
     class="cluster">
       class="card center">
        

    Card Title

    Card content

    class="card center" data-state="active">

    Active Card

    This card is active

    Enter fullscreen mode Exit fullscreen mode

    6.5 Atomic Design

    And there is a similarly named methodology called Atomic Design, which is related to the Atomic CSS concept mentioned earlier.

    It's closer to a designer-centric methodology rather than a developer-centric one.

    Atomic Design

    1. Ions: Design tokens that define basic design values such as colors, typography, and spacing.
    2. Atoms: Basic UI elements that cannot be broken down further, such as buttons, input fields, and labels.
    3. Molecules: Simple and reusable components created by combining multiple atoms.
    4. Organisms: Relatively complex UI sections made by combining molecules and atoms.
    5. Templates: Structures that form the layout of a page by arranging various organisms, molecules, and atoms.
    6. Pages: Completed UIs filled with actual content, which are the final screens that users see.

    If you want to see the differences between these at a glance, I recommend checking out the following tweet or article.

    Atomic design with real sample

    Another characteristic is the distinction between systems and products.

    System and product

    • System: A collection of reusable and general components
    • Product: A set of elements specific to a particular product or project

    7. How should styles be managed?

    This chapter aims to present a new paradigm for style management suitable for modern web development environments, based on previously discussed concepts. (image source)

    Research Paradigms

    The content is divided into three main areas:

    1. Structuring CSS methodologies and presenting mental models
    2. Proposing best practices and mental models for CSS expression
    3. An integrated approach to style management considering both areas

    First, we introduce the SCALE CSS methodology, which is intuitive and scalable, developed through the sublation of existing CSS methodologies such as ITCSS, BEM, and OOCSS. This process integrates core concepts from each methodology and explores effective ways to handle various cases.

    As discussed in Chapter 6, CSS methodologies are policies, while CSS in JS and CSS pre/post processors are mechanisms. If policies are closer to the form that contains the content, mechanisms are tools for substantiating expression.

    To focus more on methods for CSS management rather than tools, and to find points of contact with policies, we consider the formal aspects of expression rather than its substance.
    We share good practices in this regard:

    1. Design Tokens: Encoding design decisions to limit selectable values and invert dependencies.
    2. Atomic CSS: Mapping style properties and design tokens under single responsibility, reducing coupling through interface segregation.
    3. Variants: Defining component styles under the open-closed principle as 'variants' to enable Liskov substitution and increase cohesion.

    Combining these concepts, we propose StyleStack as a style management strategy. This strategy combines the consistency of design tokens, the efficiency of Atomic CSS, and the flexibility of variants to implement complex UIs faster and more consistently.

    Finally, we bring together the SCALE CSS methodology and StyleStack in formal terms, helping the methodology move towards the substantiation of form and deriving a concrete directory structure.
    Upon completing these steps, we will be prepared to discuss the implementation of CSS in JS, a tool for substantiating expression.

    7.1 Rethinking Methodologies

    When looking at the methodologies, there seems to be a sense of similarity. (image source)

    Ranking of Methodologies

    Let's organize them based on ITCSS, the most detailed methodology, with the addition of States.
    In Tools, I've included Atomic CSS, considering each as a 'function'.

    1. Settings: SMACSS's Theme, Atomic Design's Design token
    2. Tools: Atomic CSS, CUBE CSS's Utility
    3. Generic: SMACSS's Base
    4. Elements: Atomic Design's Atoms
    5. Objects: OOCSS's Container, SMACSS's Layout, CUBE CSS's Composition, Atomic Design's Templates
    6. Components: BEM's Block/Element, OOCSS's Content, SMACSS's Module, SUITCSS's Component, CUBE CSS's Block, Atomic Design's Molecules/Organisms
    7. States: OOCSS's Structure/Skin, BEM's Modfier, SMACSS's State, SUITCSS's Modfier
    8. Utilities: SUITCSS's Utility, CUBE CSS's Exception

    This classification seems to cover almost all cases.
    However, it's noticeable that in Component's Element (BEM's Element, Atomic Design's Molecules) and State, OOCSS's Structure/Skin distinction combines two concepts, and Atomic Design's Templates, which define page-level layouts, are together with Objects.

    We've encountered the concept of Component's Element before, and it's certainly a familiar concept.
    Depending on the complexity of the component, we can define it in the same file as the Block, or create a folder and define each Element and Block separately.

    Carousel Anatomy

    Now, let's consider Structure/Skin.
    It's ambiguous to add states that affect layout and decorative states as separate classifications.
    Instead, it would be better to separate them when creating each Variant, like size: "small" | "medium" | "large" and shape: "primary" | "filled" | "outlined".

    Button variants

    Atomic Design's Templates are about separating page-level layouts, not component-level layouts.
    It's the same concept as Next.js transitioning from page router to app router, where it's divided into pages and templates.
    Depending on the project, they could be managed in the same directory as pages, or collected in something like src/layouts/page-template.

    Next app router layout

    Now that we've sorted out the exceptions, let's further organize the structure and names of ITCSS.
    Let's group items that do similar things and change them to more intuitive names.

    1. Settings define static values, and Tools define dynamic functions, both meant to be imported and used, not directly affecting the app. Recently, Configs and Utils seem to be used more often.
    2. Generic's reset, normalization, and Elements' default styles are Bases styles that apply globally.
    3. Layouts is more intuitive than Objects, and Exception is better than Utilities.
    4. States are included in each Layout or Component.

    Let's apply this to ITCSS:

           /\
          /  \
         /    \  Exceptions (Styles for special cases)
        /      \
       /        \  Components (Reusable components)
      /          \
     /            \ Layouts (Layout-related styles)
    /              \
    ----------------
    |    Bases     | (Globally applied styles)
    ----------------
    |    Utils     | (Globally used utilities)
    ----------------
    |   Configs    | (Basic settings)
    ----------------
    
    Enter fullscreen mode Exit fullscreen mode
    1. Configs: Include project basic settings such as global variables, design tokens(color definitions, font settings), etc.
    2. Utils: Define globally used tools such as mixins/functions, atomic css etc.
    3. Bases: Styles to be applied globally, such as reset/normalization, and basic styles for HTML elements
    4. Layouts: Layout-related styles, can have elements and states(structure/skin), page templates
    5. Components: Reusable components, can have elements and states(structure/skin)
    6. Exceptions: Styles used exceptionally. e.g. Using values not present in design tokens, Increasing CSS specificity, Use !important

    In this article, we will refer to it as the SCALE(Scalable, Composable, Adaptive Layered Expression) CSS methodology.

    7.2 Design Tokens

    The above discussion considered the aspect of management but did not take into account the design system.

    A representative element of design systems is design tokens.
    Design tokens store repetitive design decisions as a single source of truth.

    Simply put, it's like saving a color palette from a vast color space and using it consistently across the project.
    Color space and color palette

    However, design tokens are not just simple 'settings'. They need to be set at both the app and component levels, and they function similarly to reactivity. [paste - design tokens, Tokens, variables, and styles]

    Paste design tokens

    Design token reactivity

    Design tokens, like styles, are defined from simple to specific.

    1. Primitive tokens: Define the most basic design values such as colors, fonts, sizes, spacing, etc.
      • These have the lowest level of abstraction and contain specific values.
      • Example: color-blue-500: #0000FF, font-size-16: 16px
    2. Semantic tokens: Semantic tokens are tokens that give meaning based on primitive tokens.
      • They use abstracted names that represent the purpose or intention of the design.
      • They are defined by referencing primitive tokens.
      • Example: color-primary: $color-blue-500, font-size-body: $font-size-16
    3. Component tokens: Component tokens define design values thar are applied to specific UI components.
      • These are tokens that are directly applied to actual UI components.
      • They are mainly defined by referencing semantic tokens. (Sometimes primitive tokens can also be directly referenced)
      • Example: button-primary-color: $color-primary

    Naming is important for semantic tokens.

    Semantic token

    7.3 Atomic CSS

    Atomic CSS, as we have seen earlier, is one of the major paradigms and is both a useful tool and a controversial topic.
    However, when we examine Atomic CSS, it can mean various things:

    1. Atomic CSS as usage: Can various types of visual styles be written easily in className?
    2. Atomic CSS as scope: Should it be applied globally or only to parts?
    3. Atomic CSS as a framework: Does it have a well-defined design system?
    4. Atomic CSS as output: Is it converted to single-property classes after build?

    Here, we will analyze each case and derive its advantages and disadvantages, focusing primarily on Tailwind CSS, a representative Atomic CSS Framework, while also examining why we need on-demand global atomic CSS.

    Tailwind CSS

    7.3.1 Atomic CSS as Usage

    It can be used like inline CSS.
    You don't need to name it, and it's located even closer than co-location, making it very convenient when the scale is small or when creating prototypes.

    • This greatly reduces the time spent writing separate CSS files and class names.
    • Styles can be applied intuitively.
    
    <template>
       class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
        
      
    template>
    
    Enter fullscreen mode Exit fullscreen mode

    However, it's different from inline styles:

    1. Limitations: No limitations such as media queries, selectors, pseudo-elements, animations, etc.
    2. File size: File size is reduced as it's defined only once for use.
    3. Naming: Each name is short and concise, making it good for typing.
    4. Value type: It's a string value.

    Except for number 4, all are good characteristics.
    Number 4 is a definite drawback as it's difficult to utilize type systems and highlighting.
    What if we express it in CSS in JS like this:

    css([
      {
        bg: {
          base: "blue-500",
          _hover: "blue-700",
        },
        text: "white",
        font: "bold",
        py: 2,
        px: 3,
      },
      "rounded",
    ]);
    
    Enter fullscreen mode Exit fullscreen mode

    Number 3 can also be controversial, but the bigger issue is how to define the ubiquitous language in the design system.
    If necessary, you can use the CSS property names as they are.

    Finally, the usesage of Atomic CSS itself can be controversial(1, 2).
    While it certainly has the drawback of lack of meaning, web development has changed.
    With most solutions now having the front-end directly modify HTML and CSS. The era when dynamic pages became necessary in the early days of web development, leading to the emergence of backend technologies like PHP/JSP/Django to generate HTML, and when semantic CSS was absolutely necessary for styling interfaces with the frontend has now passed.

    7.3.2 Atomic CSS as Scope

    Atomic CSS should be used globally. (image source)

    CSS scope

    Why has TailWind CSS succeeded, unlike traditional Atomic CSS?
    The most significant feature of Tailwind CSS is its utility-first approach.

    Previously, it was either used partially like Bootstrap's Helpers or had to be built from scratch.
    Partial use is literally closer to utilities or helpers, making it difficult to fully utilize the advantages of Atomic CSS, such as minimal file size increase.
    Conversely, building a comprehensive system from scratch is prone to exceptions and difficult to maintain consistently, unlike semantic methodologies that can directly utilize product domain meanings or component structures.

    However, Tailwind provides a design system that allows global use of Atomic CSS.
    It maps CSS properties to design tokens and designates all available styles as a single source of truth.

    To utilize Atomic CSS well, we should prevent one-time style additions and apply it globally!!

    7.3.3 Atomic CSS as a Framework

    To apply it globally, it's essential to have a framework that allows using predefined classes.
    This article doesn't aim to create a CSS framework itself, so we'll skip that.

    Theme UI

    However, mapping CSS properties to design tokens is an important part as a meta-design system.
    We need to provide a way to map according to the theme specifications of Theme UI or Styled System.

    7.3.4 Atomic CSS as Output

    As seen in 6.1.3 Style Output and Apply, even if the input is not Atomic CSS, Atomic CSS can be generated in the output.
    Here, we argue that we should have global atomic CSS as a single source of truth, so we'll skip this as well.

    On-demand generated

    However, generating all styles to create a single source of truth for mapping CSS properties to design tokens is inefficient.
    It will become unbearable due to the Combinatorial Explosion.

    Styles should be generated only for the rules used on-demand, according to a set of rules that map CSS properties to design tokens based on theme specifications.

    Yes, we need on-demand global atomic CSS.

    7.4 Variants

    Atomic CSS is convenient as it can be written without the need to name classes.
    However, what about when we need to handle complex states?

    Variants(Modifier) are visual representation that compresses the Option, State, and Context discussed in the design system.

    It's also known for its API introduced by Stitches.

    Stitches

    The Stitches Variants API is innovative.
    It allows for declarative description of each state style of the modifier.

    const Button = styled("button", {
      // base styles
    
      variants: {
        color: {
          violet: {
            backgroundColor: "blueviolet",
            color: "white",
            "&:hover": {
              backgroundColor: "darkviolet"
            }
          },
          gray: {
            backgroundColor: "gainsboro",
            "&:hover": {
              backgroundColor: "lightgray"
            }
          }
        }
      }
    });
    
    () => <Button color="violet">ButtonButton>;
    
    Enter fullscreen mode Exit fullscreen mode

    What's even more impressive is that you can declaratively express everything from the default value of a Variant to when interactions occur between Variants.

    const Button = styled("button", {
      ...styles,
    
      variants: {
        color: {
          violet: { ...violetStyles },
          gray: { ...grayStyles }
        },
        outlined: {
          true: { ...outlineVariants }
        }
      },
    
      defaultVariants: {
        color: "violet"
      },
    
      compoundVariants: [
        {
          color: "violet",
          outlined: true,
          css: {
            color: "blueVariantsviolet",
            borderColor: "darkviolet",
            "&:hover": {
              color: "white"
            }
          }
        },
        {
          color: "gray",
          outlined: true,
          css: {
            color: "gray",
            borderColor: "lightgray",
            "&:hover": {
              color: "black"
            }
          }
        }
      ]
    });
    
    () => (
      <Button color="violet" outlined>
        Button
      Button>
    );
    
    Enter fullscreen mode Exit fullscreen mode

    Now BEM can be expressed through the Variants API.

    1. Block: File
    2. Element: Each component
    3. Modifier: Variants included in the component

    You can immediately adhere to all constraints without relying on rule-based tools such as BEM Tools.

    In traditional Atomic CSS, a tool called CVA(Class Variance Authority) makes this possible.
    Since we will be using CSS in JS approach, our method will be slightly different.

    7.5 Layers for Style

    For the expression of style(CSS) to actually appear, there must be elements(HTML) as content, and dynamic application(JavaScript) should also be considered.
    To prevent repetitive styles, pre/post-processors or JavaScript are also needed. (image source)

    HTML, CSS, JavaScript

    This is similar to the relationship between JSX and State.

    For the expression of JSX to actually render, there must be state as dynamic content.
    CSS is essential for visual styling, as JSX and state management alone cannot fully express design aesthetics.
    To achieve consistent and maintainable styling, CSS should be strategically integrated with JSX and state management, creating a unified approach to building user interfaces.

    From a JavaScript perspective, the layers would be as follows:
    Referencing Adobe Spectrum Architecture and The future of Chakra UI.

    1. JSX: Binds HTML/Widget and JS
    2. State hook: Logic independent of view platform
    3. Behavior hook: Handles events, accessibility, internationalization, etc. for platform APIs (DOM, React Native, etc.)
    4. Headless Component: Provides binding of JSX and hooks for use
    5. Component: Component blocks including design

    From a style perspective, it appears as follows:

    Layers for Style - StyleStack

    1. Literal: CSS pre/post-processors or CSS-in-JS, considering repetitive use of CSS, etc.
    2. Theme: Design token values and customization for Color, Typography, Spaces, etc.
    3. Atomic: Atomic styles that map to visual values
    4. Variants: Styles for reusable blocks
    5. Styled Component: Binding with JSX components

    This will be referred to as StyleStack.

    7.6 Combination with Methodologies and Folder Structure

    Let's combine the StyleStack Layer with SCALE CSS methodology for management.
    The methodologies focus more on Form than Substance, while the style layers focus more on Expression than Content, so there will inevitably be some discrepancies despite seeming similar. (image source)

    Hjelmslev matrix

    If you need more explanation about the relationship between Expression-Content and Form-Substance, I recommend reading Rock-Paper-Scissors: A Linguistic Approach to Games.
    Basically, it follows the perspective of Hjelmslev's semiotics.

    1. Literal: Closer to elements constituting Substance, so it's not here.
    2. Theme: Corresponds to Configs.
    3. Atomic: Belongs to Utils, right?
    4. Variants: Modifiers, so they'll belong to Layouts and Components.
    5. Styled Components: Also close to binding to Elements, so they'll belong to Layouts and Components.

    Bases and Exceptions don't have separate matches.
    Bases are Content made from Literal, and Exceptions are closer to Content that occurs at the component level.

    For StyleStack layers and SCALE CSS methodology to complement each other, they should each have Content and Substance, right?
    For style layers to have Content, people need to write the content, so there's nothing to do right away.

    On the other hand, how about making the methodology into Substance?
    It's possible to some extent by creating a Form for the folder structure.

    1. Configs: It's better to separate global variables and design tokens.
    2. Utils: It's better to separate Atomic CSS (core-style) and simple utilities.
    3. Bases: Usually, reset/normalization is imported from libraries, so it's okay to define it together in global css.
    4. Layouts: Use as is. However, for page templates, as mentioned earlier, using app/ or layouts/page-template is also good.
    5. Components: Use as is.
    6. Exceptions: In cases where !important is used, gathering and managing them together is one approach.
    src/
    ├── styles/
    │   ├── constants/: Configs - Global variables
    │   ├── theme/: Configs - Design Tokens definitions
    │   ├── core-styles/: Utils - Atomic CSS definitions
    │   ├── utils/: Utils - Mixins and style-related functions
    │   └── expetions/: Exeptions - Collection of exceptions
    ├── app/
    │   ├── layout.tsx: Layouts - Page templates
    │   ├── page.tsx
    │   ├── global.css: Bases - Normalization and global styles
    │   └── [pages]/
    ├── assets/
    ├── components/
    ├── layouts/
    └── .../
    
    Enter fullscreen mode Exit fullscreen mode

    Now, let's focus more on Expression(StyleStack layer) and think about it.

    8. Why CSS in JS?

    Now that we've finished defining the StyleStack layer, we need to implement a natural connection from Literal to Styled Component. (image source)

    CSS in JS

    First, should we use pre/post-processors or Javascript to extend Literal CSS?
    One of the original advantages of CSS in JS, isolation, has been offset by CSS Modules and @layer.
    Conversely, since the features of post-processors can be used in CSS in JS, we don't consider them.

    8.1 Preprocessors

    1. Learning curve: Since it's an extension of CSS, it's easy to adapt and existing CSS code can be used as is.
    2. Highlighting and auto-completion: You can use the highlighting and auto-completion provided by the editor as is.
    3. Mixins and CSS-specific syntax: Control flow and @content allow you to easily add styles just by declaring styles where needed without object manipulation or returns. Less's property merging and Stylus's Property lookup are also interesting features.
    4. CSS-specific utilities: It includes features like SASS's color module and calc() optimization.
    5. Ecosystem: There's infrastructure like Stylelint and Critical CSS.

    8.2 CSS in JS

    1. Code sharing: You can immediately use all of Javascript's functions, variables, and rich libraries.
    2. Javascript: You only need to learn Javascript. Also, Javascript provides more robust capabilities for complex object manipulation and flow control compared to preprocessor.
    3. References: Dependencies are explicit, and features like go-to-definition and refactoring can be used immediately. Vue partially supports this, but it's not possible when nested. It's also a great help in removing zombie code.
    4. Dynamic styles: Features like values changing according to props or compound variants are easy to write and integrate in CSS in JS.
    5. Co-location: You can write styles in the same file as the component.

    8.3 So, Why CSS in JS

    Many of the advantages available in preprocessors can be relatively easily achieved by investing in the tooling infrastructure of CSS in JS.

    • Highlighting and auto-completion: It's possible with tools like vscode-styled-components, and there are precedents for copy and paste as well.
    • CSS-specific utilities: Can be complemented with a nice library called polished.
    • Ecosystem: linaria has proven that source maps, linting, etc. can be used.

    With a little effort, you can even mimic usage similar to @mixin.

    function mediaQuery(breakpoint) {
      const breakpoints = {
        tablet: "@media (min-width: 768px) and (max-width: 1023px)",
        desktop: "@media (min-width: 1024px)",
      };
    
      if (breakpoint in breakpoints) {
        return (styles) => ({
          [breakpoints[breakpoint]]: styles,
        });
      } else {
        // or use throw
        console.warn("Unknown breakpoint: " + breakpoint);
        return () => ({});
      }
    }
    
    // Usage example
    const responsiveElement = css({
      fontSize: 16px,
      ...mediaQuery("tablet")({
        fontSize: "18px",
      }),
      ...mediaQuery("desktop")({
        fontSize: "20px",
      }),
    });
    
    Enter fullscreen mode Exit fullscreen mode

    Of course, go-to-definition is not exclusive to CSS in JS. It's possible through CSS Module extensions.
    However, there are many constraints in sharing Javascript variables and using libraries, dynamic styles, and co-location (possible in SFC).
    Crucially, there's also the point that JavaScript runtime is ultimately necessary for creating Styled Components, which is the final step of StyleStack.

    I think there's still a lot of room for improvement in CSS in JS infrastructure, and it has relatively fewer constraints.
    For these reasons, despite quite liking preprocessors like Sass, I believe investing in CSS in JS is the right path.

    9. Introduce project mincho

    I would like to introduce you to the project that will proceed based on the content discussed so far.

    • mincho-js/mincho

    Mincho Logo

    9.1 Motivation

    The original motivation for starting my project was to unify the Atomic CSS and SemanticCSS methodologies. From a semiotic approach, I believed that this could be sufficiently represented as Expression and Content using the Sign function.

    Sign function

    The project name was decided early on.
    In Korea, mint chocolate is abbreviated as 'Mincho(민초)', and along with vanilla, it's one of my favorite ice cream flavors.
    Mint chocolate is unique in its combination of contrasting flavors, harmonizing the cool and refreshing taste of mint with the sweet and smooth taste of chocolate.
    This is where the idea came from:

    Mincho bongbong icecream

    1. The combination of Atomic CSS (Good) + Variant (Good) = Better resembles mint chocolate.
    2. I planned to base it on a library called Vanilla Extract.
    3. It's a flavor I like. The most personal is the most creative, isn't it? 😹

    9.2 Why use Vanilla Extract?

    Vanilla Extract was chosen as the foundation for this project for several reasons.

    Vanilla extract

    1. Type-Safe: It was created with a focus on type safety.
    2. Zero-runtime: As mentioned in '6.1.1 Integration', this is very important considering the future of RSC.
    3. API: The basic design, such as the Composition API, is very clean.
    4. Features: Existing features like Style, StyleVariants, Theme, and Recipe are sufficiently powerful and worth referencing.
    5. Constraints: Prevents encapsulation-breaking operations like .selector > * at build time.
    6. Potential: Although co-location is not currently available, referring to Macaron suggests it should be possible.

    9.3 Brief Plan

    We cannot provide all features from the beginning, we plan to achieve them step by step.

    1. Natural CSS in TypeScript: We will bind various CSS preprocessing features to be specialized for TypeScript.
    2. A CSS in JS for Scalable: Supports and integrates StyleStack(Theme, Atomic CSS, Variants, and Styled Component) layers, making it possible to manage large-scale styles.
    3. Build your own design system: It functions as a framework for creating design systems through Figma plugins and Document system, among others.

    As of writing this, the 'Natural CSS in TypeScript' phase is nearing completion, and I hope to achieve A CSS in JS for Scalable within this year.

    As it is currently a Work In Progress (WIP), the API is unstable.
    A major API overhaul is planned around the completion of the A CSS in JS for Scalable phase, which will comprehensively consider a small API surface, improved type safety, SSR (and Server Component) support, and Vanilla Extract compatibility.
    For example, while we strive to ensure maximum compatibility with Vanilla Extract's usage, differences may arise as the project progresses, and there are inevitable differences in how things operate.
    Therefore, we intentionally named the functions differently from Vanilla Extract(generally making them more concise), and we plan to create a separate compat package for compatible APIs.

    Below is a describe of our ideas regarding the direction of StyleStack's API design and subsequent plans.

    10. CSS-friendly CSS in JS

    What does it mean to be specialized in CSS processing?
    Besides the tooling infrastructure mentioned earlier, we can also consider syntax optimized for CSS.

    Let's imagine CSS in JS from scratch according to various conditions.

    10.1 Template String vs Object Style

    • Template Strings vs. Objects in CSSinJS

    In the world of CSS In JS, there are two ways to express CSS:
    Template string and Object style.

    Template strings seem better for simple use.
    This is because you can use CSS properties and syntax as they are.

    Template string and Object style

    However, the object style is the same expression method as CSSOM and is a better method when a lot of interpolation is needed.

    lot of interpolation is needed

    When using TypeScript, there is also the advantage of supporting types.
    The reason for using CSS In JS is basically to handle many rules and to interact with JS.
    Therefore, when using CSS In JS, it's recommended to primarily use the Object style.

    If there are few design rules and interaction with JS is unnecessary, it's better to use a CSS preprocessor.

    10.2 Overcoming Syntax

    The fundamental limitation of CSS In JS is that CSS syntax is not literal.

    Because of this, color codes basically don't work with pickers in editors, and CSS units (px, rem, vw, etc.) and values (visible, space-between, etc.) cannot be used without treating them as strings.

    const sample = css({
      // Color
      color: "red",
      background: "#EEEEEE",
    
      // Unit
      fontSize: "16px",
      fontSize: "1rem",
    
      // Value
      visibility: "visible",
      justifyContent: "space-between"
    });
    
    Enter fullscreen mode Exit fullscreen mode

    Fortunately, there is an extension for color picker.

    For properties that receive common values like px, you could define them like JSS's default-unit or Vanilla Extract's unitless property.

    const sample = css({
      // cast to pixels
      padding: 10,
      marginTop: 25,
    
      // unitless properties
      flexGrow: 1,
      opacity: 0.5
    });
    
    Enter fullscreen mode Exit fullscreen mode

    10.3 SCSS and Nested Syntax

    The area where SCSS took the lead is nested syntax.
    As you've seen earlier, it supports attribute nesting, parent selector nesting using &, and even nesting of @at-rules like @media.

    .sample {
      width: 200px;
      font: {
        /* Property Nested */
        size: 10px;
        weight: bold;
      }
    
      /* Selector Nested */
      a & {
        background: grey;
      }
      &:hover {
        background: blue;
      }
    
      /* At rule Nested  */
      @media (prefers-color-scheme: dark) {
        color: red;
      }
    }
    
    Enter fullscreen mode Exit fullscreen mode

    This is made possible by using several rules:

    • Nesting occurs when it's an object rather than a regular value, and since kebab-case is converted to camelCase, nested properties also follow the rules
    • If an attribute contains & like in Emotion, it is treated as a selector
    • Simple selectors like "&:hover" can be used as :hover, reducing unnecessary &, similar to vanilla extract's Simple pseudo selectors, for Pseudo-classes and Pseudo-elements
    const sample = css({
      width: 200,
      font: {
        Size: 10,
        Weight: "bold"
      },
    
      "a &": {
        background: "grey"
      },
      ":hover": {
        background: "blue"
      },
    
      "@media (prefers-color-scheme: dark)": {
        color: red;
      }
    });
    
    Enter fullscreen mode Exit fullscreen mode

    Let's think about how to make it a little more convenient.
    Like Panda CSS's Conditional Styles, we can use _ instead of : and use camelCase to make it immediately usable in properties. (Only _ and $ are special characters that can be used immediately in JavaScript properties)

    If there's an issue with the Panda CSS approach, it's the ad-hoc nature of inserting arbitrary classes, such as _dark meaning &.dark, .dark &.
    This might be acceptable for framework presets, but it's not suitable for applying as a literal syntax.

    const sample = css({
      background: "#EEEEEE",
      _hover: {
        background: "blue"
      },
      __before: {
        background: "grey"
      },
    
      // Or
      background: {
        base: "#EEEEEE",
        _hover: "blue",
        __before: "grey"
      }
    });
    
    Enter fullscreen mode Exit fullscreen mode

    When nesting at-rules, we could group them together if they're the same rule, or we could utilize Conditional Styles.

    const sample = css({
      background: "#EEEEEE",
      "@media": {
        "(min-width: 768px) and (max-width: 1023px)": {
          background: "blue"
        },
        "(min-width: 1024px)": {
          background: "grey"
        }
      },
    
      // Or
      background: {
        base: "#EEEEEE",
        "@media (min-width: 768px) and (max-width: 1023px)": "blue",
        "@media (min-width: 1024px)": "grey"
      }
    });
    
    Enter fullscreen mode Exit fullscreen mode

    Of course, excessive use of nesting can cause problems, so it would be good to set a maximum nesting count as an ESLint rule or similar.

    10.4 Less and Merge properties

    One of the main characteristics of Less is that it's lazy.
    However, since JavaScript is strict by default, I think this approach is not suitable for applying to CSS in JS.

    However, the Merge properties feature appears to be quite useful.
    This is because long properties like box-shadow or transform were difficult to write in a single line.

    .sample {
      /* Merge with Comma */
      box-shadow+: inset 0 0 10px #555;
      box-shadow+: 0 0 20px black;
    
      /* Merge with Space */
      transform+_: scale(2);
      transform+_: rotate(15deg);
    }
    
    Enter fullscreen mode Exit fullscreen mode
    .sample {
      box-shadow: inset 0 0 10px #555, 0 0 20px black;
      transform: scale(2) rotate(15deg);
    }
    
    Enter fullscreen mode Exit fullscreen mode

    As mentioned earlier, only _ and $ can be used as special characters in JavaScript.
    Since space is similar to _, it would be good to add $ to commas.

    • Since you can't have the same key multiple times, values are represented as arrays
    • If it ends with $, join with ,, if it ends with _, join with a space
    const sample = css({
      boxShadow$: ["inset 0 0 10px #555", "0 0 20px black"],
      transform_: ["scale(2)", "rotate(15deg)"]
    });
    
    Enter fullscreen mode Exit fullscreen mode

    10.5 Stylus and References

    Stylus provides several features for referencing.
    Both Partial Reference and Relative Reference seem to have a high potential for causing mistakes.
    Moreover, given the nature of CSS in JS, which is designed around components and doesn't require much nesting, the likelihood of needing these is low.

    However, property value referencing could be beneficial.
    It seems useful as it can eliminate unnecessary variables, and if there's no reference target, it can be displayed as an error at build time.

    const sample = css({
      width: 100,
      marginLeft: "@width",
      marginTop: "calc(-(@width / 2))"
    });
    
    Enter fullscreen mode Exit fullscreen mode

    9.6 PostCSS and CSS Extensions

    PostCSS uses standard CSS syntax, so it can be used together with CSS in JS.

    Among them, postcss-preset-env extends various syntaxes by default.
    For example, there are convenient media query ranges.

    const sample = css({
      "@media (480px <= width < 768px)": {
        fontFamily: "system-ui"
      }
    });
    
    Enter fullscreen mode Exit fullscreen mode
    @media (min-width: 480px) and (max-width: 767.98px) {
      .sample {
          font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
      }
    }
    
    Enter fullscreen mode Exit fullscreen mode

    11. Scalable CSS in JS

    In CSS-friendly CSS in JS, we explored methods to appropriately express CSS in Javascript.
    Now, we need to manage CSS interactions with other CSS and Javascript.

    Since runtime caused problems with scalability, we will try to deal with Zero runtime or Near Zero Runtime cases whenever possible.

    11.1 Composition

    In practice, using multiple CSS classes together is more common than applying just one.

    In this case, we can use Composition.
    If you want to focus only on class names, clsx is also a good option.

    const base = css({ padding: 12 });
    const primary = css([base, { background: "blue" }]);
    const secondary = css([base, { background: "aqua" }]);
    
    Enter fullscreen mode Exit fullscreen mode
    .styles_base__1hiof570 {
      padding: 12px;
    }
    .styles_primary__1hiof571 {
      background: blue;
    }
    .styles_secondary__1hiof572 {
      background: aqua;
    }
    
    Enter fullscreen mode Exit fullscreen mode

    Vanilla Extract's method excellently reduces CSS output.
    However, there are specificity issues. It would be more intuitive if the last style was always applied, as it is in Emotion.

    const danger = css({
      color: "red"
    });
    const base = css({
      color: "yellow"
    });
    const sample = style([base, danger]); // `base` is applied
    
    Enter fullscreen mode Exit fullscreen mode

    We can partially solve this by declaring and merging objects, or by directly specifying the color(since .sample is declared after .base). However, we need to consider the potential increase in output size with these approaches.

    const danger = {
      color: "red"
    };
    const base = {
      color: "yellow"
    };
    const sample = style([base, danger]);
    
    // or
    const base = css({
      color: "yellow"
    });
    const sample = style([base, {color: "red"}]);
    
    Enter fullscreen mode Exit fullscreen mode

    Since Atomic CSS also has the Shorthand-Longhand problem, we should always keep specificity issues in mind when dealing with composition.

    11.2 UI = f( State )

    As React and Flutter assert, UI can be thought of as a function.

    UI = f( State )

    Then, is there any reason why Style shouldn't be a function?
    This is the same approach as Fela's principles, and following this philosophy, Flutter implements even Padding as a widget.

    const sample = css((props) => ({
      color: "red",
      background: props.bgColor
    }));
    
    function Sample() {
      return <div className={sample({ bgColor: "blue" })}>contentdiv>;
    }
    
    Enter fullscreen mode Exit fullscreen mode
    .sample {
       color: red;
       background: var(--sample-bgColor)
    }
    .SampleComponent.sample {
      --sample-bgColor: blue;
    }
    
    Enter fullscreen mode Exit fullscreen mode

    It's not good to have different usages or return values from a single function.
    Therefore, interfaces should be separated.

    // Return string(class name)
    const static = css({
      color: "red",
    });
    
    //  Return function
    const dynamic = rules(({ color }) => ({
      color,
      background: ({ bg } = { bg: "blue" }) => bg
    }));
    
    Enter fullscreen mode Exit fullscreen mode

    11.3 Making it Declarative

    The previous example would work well in CSS in JS with runtime, but not in zero-runtime CSS in JS.
    As seen in the cases of StyleX and PigmentCSS, dynamic styles are quite limited.

    const dynamic = rules(({ color }) => ({
      color,
      background: ({ bg } = { bg: "blue" }) => bg
    }));
    
    Enter fullscreen mode Exit fullscreen mode

    Also, traversing the AST to evaluate dynamic styles is a rather arduous task.
    Is it possible to create a declarative approach similar to the Variants example previously mentioned as an effective declarative API?
    The above cases could be represented as follows:

    const dynamic = rules({
      props: ["color", { bg: { base: "blue", target: "background" }}]
    });
    
    Enter fullscreen mode Exit fullscreen mode

    You might want arbitrary Props only when using specific variants.

    // Same behavior
    const sample1 = rules((props) => {
      if("color" in props) {
        if(props.color == "white") {
          return ({
            color: "#EEEEEE",
            background: props.background
          });
        }
      }
    });
    
    const sample2 = rules({
      variants: {
        color: {
            white: [{ color: "#EEEEEE" }, "background"]
          }
        }
      }
    });
    
    sample2({ color: "white", background: "red" });
    
    Enter fullscreen mode Exit fullscreen mode

    It would be convenient if we could create Styled Components with Props specified based on Rules, wouldn't it?

    const Container = styled.div([
      sample2,
      {
        display: "flex",
        variants: { vertical: { true: { flexdirection: "column" } } },
      },
    ]);
    function Sample() {
      return (
        <Container color="white" background="black" vertical>
          text
        Container/>
      );
    }
    
    Enter fullscreen mode Exit fullscreen mode

    By creating it this way, you can make styled components as conveniently as with Styled System.

    11.4 Atomic CSS

    We've briefly explored simple and dynamic concepts, and addressed Variants, which transformed BEM, a prominent semantic CSS methodology, into a declarative approach.

    Recall the primary objective of Atomic CSS in comparison to semantic CSS.

    Styles should be generated only for the rules used on-demand, according to a set of rules that map CSS properties to design tokens based on theme specifications.

    How can we ensure that styles are generated only for the rules being used?
    Looking at various issues(#91, #992, #1132, #1237) with Vanilla Extract, it doesn't seem easy.

    A potential approach involves creating rules and utilizing them with the previously defined css(), rules(), and styled() functions, while caching each property-value pair.
    By creating it this way, you can achieve Tailwind's usage and Windi CSS's Attributify Mode in a near on-demand manner without the complex regular expressions of UnoCSS.

    const { css, rules, styled } = defineRules({ /* Something */ });
    
    const sample1 = css(["inline", { px: 2, c: "indigo-800" }]);
    const sample2 = rules({
      px: 4,
      variants: {
        color: {
            white: [{ c: "white-100" }, "bg"]
          }
        }
      }
    });
    const Sample3 = styled.div([saple2]);
    
    Enter fullscreen mode Exit fullscreen mode

    While '11.1 Composition' may cause specificity issues, it can mitigate the problem by behaving similarly to tailwind-merge at build time.

    While not yet fully established, we can propose a Draft API as follows:
    When strict mode is true, only shortcuts and toggles can be used instead of properties.

    const { css, rules, styled } = defineRules({
      // Whether to allow properties attributes - allow only toggles or shortcuts
      strict: true,
    
      // Restrictions on features available in CSS
      properties: {
        // Allow only values in arrays
        display: ["none", "inline"],
        paddingLeft: [0, 2, 4, 8, 16, 32, 64],
        paddingRight: [0, 2, 4, 8, 16, 32, 64],
    
        // Allow only values in objects
        color: { "indigo-800": "rgb(55, 48, 163)" },
    
        //Entire properties
        background: true,
        border: false
      },
      shortcuts: {
        pl: "paddingLeft",
        pr: "paddingRight",
        px: ["pl", "pr"],
        c: "color",
        bg: "background"
      },
      toggles: {
        inline: { display: "none" }
      }
    });
    
    Enter fullscreen mode Exit fullscreen mode

    However, mapping every element individually may prove cumbersome.
    The availability of predefined sets, similar to the ThemeUI spec, would greatly enhance convenience.
    You might want to map not only the property values but also shortcuts and toggles like tailwind.

    const { css, rules, styled } = defineRules({
      // Restrictions on features available in CSS
      properties: themeUISpec({
        colors: { "indigo-800": "rgb(55, 48, 163)" }, // color, background, accentColor, ...
        space: [0, 2, 4, 8, 16, 32, 64] // margin, padding, inset, top, ..
      }),
       ...twindShorcutSpec() // Predefined shortcuts and toggles like the tailwind API
    });
    
    Enter fullscreen mode Exit fullscreen mode

    The provision of such diverse presets would significantly simplify the creation and utilization of Atomic CSS.
    And, like PandaCSS's Conditional Styles, it will be necessary to provide various conditions.

    const { css, rules, styled } = defineRules({
      defaultCondition: ["light", "vertical"],
      conditions: {
        light: "",
        dark: ["&.dark", ".dark &"],
        osLight: {},
        osDark: { "@media": "(prefers-color-scheme: dark)" },
        horizontal: "&[data-orientation=horizontal]",
        vertical: "&[data-orientation=vertical]"
      },
      compoundConditions: [
        {
          when: ["light", "osLight"],
          condition: "&.pureLight"
        },
        {
          when: ["dark", "osDark"],
          condition: "&.pureDark"
        }
      ]
    });
    
    Enter fullscreen mode Exit fullscreen mode

    11.5 Theme

    Let's discuss the Theme concept mentioned in Atomic CSS in more detail.
    Themes are mappings of design tokens, and as we saw in Atomic CSS, they typically have two structures.
    Of course, we could also follow the W3C Design Token Format.

    const theme = {
      spaces: [0, 2, 4, 8, 16, 32, 64],
      colors: {
        "black-100": "#000",
        "white-100": "#fff",
        "blue-500": "#07c"
      }
    };
    
    Enter fullscreen mode Exit fullscreen mode

    We need to consider the scalability across primitive tokens, semantic tokens, and component tokens.
    For primitive tokens, we can create subgroups for categories like color:

    const [, tokens] = theme({
      spaces: [0, 2, 4, 8, 16, 32, 64],
      colors: {
        black: {
          100: "#000"
          // ...
        },
        white: {
          100: "#fff"
          // ...
        },
        blue: {
          // ...
          500: "#07c"
        }
      }
    });
    
    Enter fullscreen mode Exit fullscreen mode

    Let's consider semantic tokens.
    While they can be isolated like component tokens, semantic tokens are often used globally at the application level.
    Therefore, we need to design them to handle derivable tokens.

    const [, tokens] = theme({
      // Main values
      values: {
        spaces: [0, 2, 4, 8, 16, 32, 64],
        colors: {
          black: {
            100: "#000",
            // ...
          },
          white: {
            100: "#fff"
            // ...
          },
          blue: {
             // ...
            500: "#07c"
          }
        }
      },
    
      // Derived values
      alias: ({ spaces, colors }) => ({
        spaces: {
          small: spaces[2],
          medium: spaces[3],
          large: spaces[5]
        },
        colors: {
          text: colors["white-100"]
          primary: colors["blue-500"]
        }
      })
    });
    
    Enter fullscreen mode Exit fullscreen mode

    Component tokens should be isolated by component and be derivable.

    const [, tokens] = theme({ /* Something */ });
    const buttonPrimary = token(tokens.colors.primary);
    
    Enter fullscreen mode Exit fullscreen mode

    While it may seem we've covered most aspects of tokens, there's more to consider.
    We also need to consider applying tokens in modes (conditions) such as Dark and Light.
    It would be fine to manually combine Light and Dark in css() or rules(), but if we want values that automatically switch based on conditions, we run into circular dependency issues with Atomic CSS(defineRules).

    I'm currently considering how to express this aspect more elegantly.
    However, if we forgo automatic conversion based on conditions, the current structure should suffice.

    const { css } = defineRules({
      // ...
      properties: {
        paddingLeft: tokens.spaces,
        paddingRight: tokens.spaces,
        color: tokens.colors,
      }
      // ...
    });
    
    Enter fullscreen mode Exit fullscreen mode

    A potential solution is to make the result of defineRules() include theme() as well.

    11.6 Looking Back

    It seems we've achieved to some extent the concept of the StyleStack layers we defined earlier, '7.5 Layers for Style'.

    Layers for Style - StyleStack

    1. Literal: Provides various CSS-specific syntax of CSS preprocessors, considering the syntactic limitations of JavaScript. Use css().
    2. Theme: Design token values and customization for Color, Typography, Spaces, etc. Use theme().
    3. Atomic: Atomic styles that map to visual values. Use defineRules()
    4. Variants: Styles for reusable blocks. Use rules()
    5. Styled Component: Binding with JSX components. Use styled()

    We have created a hypothetical syntax and library API that is declarative and easy to write/manage.
    Personally, I find the result clean and appealing, and there are several principles behind it:

    1. Be declarative rather than listing out logic
    2. APIs for each layer should be isomorphic
    3. Expression and content presuppose each other, so they must be considered
    4. The law of excluded middle applies when hierarchies(perspectives) differ

    As a result, the API is very clean and consistent.

    // Theme Syntax
    const [, tokens] = theme({
      spaces: [0, 2, 4, 8, 16, 32, 64],
      colors: {
        black: {
          100: "#000"
          // ...
        },
        white: {
          100: "#fff"
          // ...
        },
        blue: {
          // ...
          500: "#07c"
        }
      }
    });
    const buttonPrimary = token(tokens.colors.primary);
    
    // Literal Syntax
    const base = css({ color: "#444" });
    const sample1 = rules({ props: { bg: "background" });
    const sample2 = css(["pure-class-name", base, sample1({ bg: buttonPrimary }), { width: 10 }]);
    
    // Atomic Syntax
    const { css: atomic } = defineRules({
      properties: themeUISpec(tokens),
       ...twindShorcutSpec()
    });
    const sample3 = atomic(["uppercase", { px: 4, _hover: { bg: buttonPrimary } }]);
    
    // Variants Syntax
    const variants = rules({
      // ... 
      variants: {
        outlined: {
          true: {
            border: "1px solid currentColor",
            background: "transparent"
          },
        },
        accent: {
          // ...
          pink: {
            backgroundColor: "pink",
            color: "white"
          },
        },
      }
    });
    const sample4 = variants(["outlined", { accent: "pink" }]);
    
    // Atomic Variants Syntax
    const { rules: atomicRules } = defineRules({
      properties: themeUISpec(tokens),
       ...twindShorcutSpec()
    });
    const atomicVariants = atomicRules({
      // ... 
      variants: {
        outlined: {
          true: {
            border: ["base", "current"],
            bg: "transparent"
          },
        },
        accent: {
          // ...
          pink: {
            bg: "pink-500",
            text: "white"
          },
        },
      }
    });
    const sample5 = atomicVariants(["outlined", { accent: "pink" }]);
    
    // Styled Component Syntax
    const Sample6 = styled.button([sample4, { /* Something like rules() */ }]);
    const element1 = <Sample6 outlined accent="pink">ButtonSample5>;
    
    // Atomic Styled Component Syntax
    const { styled: atomicStyled } = defineRules(/* Something like atomicRules() */);
    const Sample6 = atomicStyled.div([sample5, { /* Something like atomic rules() */ }]);
    const element2 = <Sample6 px={4} bg="indigo-600" />;
    
    Enter fullscreen mode Exit fullscreen mode

    Above all, it is possible to use only the parts of StyleStack with "as much abstraction as you want".
    This is an important characteristic that enables progressive enhancement and makes scalability and migration easier.

    12. CSS in JS for Design Systems

    It seems quite good for programmers to manage styles.
    Then, how about collaborators? (image source)

    Design system

    Designers primarily focus on making design decisions and creating the visual aspects of components and applications, rather than coding.
    This leads to some discrepancies in integration with design tools, documentation, and so on.

    Not only designers but also marketers can be considered.
    Brand guidelines should be integrated into the style guide, and they will want to see A/B tests and user feedback on UI/UX at a glance.
    They may also want to use design assets and templates for newsletters, social media posts, and banners, as well as manage styles for each marketing campaign.

    There's also a need to follow certain rules with other developers.
    For example, the depth of nested styles and the order of style alignment mentioned earlier.

    12.1 Figma

    Among the various design tools available, Figma is widely recognized as one of the best for UI design. Therefore, this section focuses on Figma-based workflows. (image source)

    Figma logo

    One of the problems with design was the Combinatorial Explosion.
    Let's take an example.
    GitHub's button component has the following conditions:

    • Structures: It has leadingVisual, label, trailingVisual, and trailingAction elements, which can appear simultaneously. However, there's no case where all elements are absent.
    • Shapes: There are Primary, Secondary, Invisible, and Danger colors.
    • States: There are Rest, Hover, Disabled, Pressed, and Focus states.
    • Contexts: There are Inactive and Loading contexts.
    • Color Scheme: There are support light and dark mode

    If we calculate the number of cases, it's (24−1)×4×5×2×2=1200.
    However, no designer would want to create 1200 components just for buttons.

    Therefore, I'd like to have the following flow:

    1. Designers and developers discuss components together(Need wireframe).
    2. Designers create components including major visual options, while developers implement functionality. After finishing, generate initial styles using Figma to Code.
    3. After receiving an explanation from the designer, the developer analyzes the visual decisions and performs state compression to reduce the number of cases as much as possible,
    4. After applying design tokens, etc., create it with Code to Figma
    5. The designer checks if the component has been created as intended, and if there are any missing rules, updates the representative component.
    6. Obtain implementation information for that component with Figma to Code
    7. Apply visual rules and then update the rest of the components with Code to Figma

    This requires two main plugins. (image source)
    Figma plugin

    Fortunately, there seem to be many examples of Figma to Code.

    • Code to Figma: Code to Design, UxPin, Storybook
    • Figma to Code: Anima, Builder.io, Grida, Figma-Low-Code

    In addition to these plugins, AI-powered tools can significantly enhance the process of modifying and creating designs. For instance, text-to-figma allows designers to generate Figma designs from text descriptions, streamlining the design process.

    To ensure smooth integration with Figma, particularly with features like AutoLayout, Constraints, Position, an opinionated CSS framework might be necessary.
    This framework would need to be compatible with Figma's design paradigms while also being suitable for code-based development. A detailed discussion on suitable CSS frameworks for Figma integration will be addressed in a separate article to maintain conciseness in this overview.

    12.2 Documentation

    Style guides are typically created by designers to ensure consistency in design across a project or organization.
    To facilitate the creation and maintenance of these style guides, easy-to-use documentation tools are essential.
    Zeroheight, Frontify, and Supernova are representative examples. (image source)

    Document cover page

    For developers' component documentation, there are Storybook, Histoire, Ladle, and Pattern Lab, but they are insufficient for designer.

    The documentation system should at least be capable of the following:

    1. Displaying design tokens and code sample together
    2. Providing and testing interactive component examples with states
    3. Explaining design intentions and technical implementation details in parallel
    4. Interactive document editor for easy modifications
    5. Search integrated with something like Algolia search
    6. Version control tracking
    7. Integrated with Figma

    Particularly for version control tracking, integration with Git is necessary, similar to i18n services(crowdin, lokalise, pontoon .etc).

    If we expand a bit further, we could support the following:

    1. Tools: Internationalization(i18n) support, A/B Testing, responsive testing, accessibility checks, and performance testing tool integration.
    2. Dashboards: For style usage statistics and user behavior patterns.
    3. Collaboration and communication: Such as real-time comments/feedback and task assignment and tracking.
    4. Manage: Role-based access control system.
    5. Authoring tools: Allow content writers to instantly create outputs according to templates.
    6. Custom: Arrange and decorate the page. You might also want to include a style tile(1, 2).

    12.3 Development Tools

    For a comprehensive design system, a robust tooling infrastructure for developers is essential, including: (image source)

    Tailwind VS Code Extension

    1. Integration of tools such as Stylelint and Sourcemap
    2. ESLint plugins/rules for CSS in JS
    3. Highlighting and auto-completion in the editor's value string at the same level as CSS, and conversion between kebab-case and camelCase
    4. Visual regression testing tools integrated with Figma/Storybook
    5. Inspector like unocss

    It's still in a very early stage, and the API is unstable.
    However, I hope that someday the API will stabilize and tooling infrastructure will be provided, allowing everyone to freely express styles and easily create and maintain design systems!

    Thank you for reading this long post.
    If you found this project interesting or helpful, I would appreciate if you could star the project.
    If you'd like to test it out and help us improve it, please reach out on Twitter or email me at [email protected].

    More References: (Unfortunately, the articles/videos are in Korean)

    • Understanding why CSS has become difficult through its history
    • Design Systems, Beyond Form: Video / Presentation
    • Cross-Platform Design System, A 1.5-Year Record: Video / Presentation
  • 8 Most Customizable UI Libraries for Next.js
    3 projects | dev.to | 10 Sep 2024
    1. Tailwind CSS
  • How to Make In-App Payments Using the Rapyd Bank Transfer API
    3 projects | dev.to | 10 Sep 2024
    Basic knowledge of Tailwind CSS.
  • Two Years Later: Not Another Portfolio Refresh
    2 projects | dev.to | 9 Sep 2024
    I've also stuck with Tailwind. This framework's utility-first approach just works, making it quick and painless to go from a basic prototype to something polished and production-ready. I honestly can't see myself moving away from it anytime soon—it's become an essential part of my toolkit.
  • Getting started with Shadcn/UI in React: A practical guide
    5 projects | dev.to | 9 Sep 2024
    In this practical guide, let us explore some of its key features, how to get started, and implement some hands-on examples. To fully understand the contents of this guide, you should have a good understanding of React and Tailwind CSS.
  • Building a Personal Finance App with Arcjet
    9 projects | dev.to | 6 Sep 2024
    This article will serve as a guide on how to create a simple finance management app using Arcjet, Next.js, Auth.js, Prisma, SQLite, and Tailwind CSS. Building a personal finance management app with modern web development skills, practical functionality, and robust security helps drive home the point of how effective it can be to incorporate these features. Our application will be proof of concept, so there will be no live working payment gateway.
  • My Personal Intro to TailwindCSS
    1 project | dev.to | 3 Sep 2024
    Tailwind documentation: https://tailwindcss.com/
  • Casual Clothes App Using Next.js 14, TypeScript, Prisma & Next-Auth
    5 projects | dev.to | 1 Sep 2024
    TailwindCSS
  • How to setup standalone CLI: use Tailwind CSS without Node.js in Shopify.
    1 project | dev.to | 31 Aug 2024
    TailwindCSS: A utility-first CSS framework for rapidly building custom designs.

What are some alternatives?

When comparing bootstrap-vue and Tailwind CSS you can also consider the following projects:

primevue - Next Generation Vue UI Component Library

flowbite - Open-source UI component library and front-end development framework based on Tailwind CSS

vite - Next generation frontend tooling. It's fast!

antd - An enterprise-class UI design language and React UI library

bootstrap-vue-next - Early (but lovely) implementation of Vue 3, Bootstrap 5 and Typescript

unocss - The instant on-demand atomic CSS engine.

Quasar Framework - Quasar Framework - Build high-performance VueJS user interfaces in record time

windicss - Next generation utility-first CSS framework.

ant-design-vue - 🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜

emotion - 👩‍🎤 CSS-in-JS library designed for high performance style composition

buefy - Lightweight UI components for Vue.js based on Bulma

Material UI - Ready-to-use foundational React components, free forever. It includes Material UI, which implements Google's Material Design.

InfluxDB - Purpose built for real-time analytics at any scale.
InfluxDB Platform is powered by columnar analytics, optimized for cost-efficient storage, and built with open data standards.
www.influxdata.com
featured
SaaSHub - Software Alternatives and Reviews
SaaSHub helps you find the best software and product alternatives
www.saashub.com
featured