Managing Style Files

When building modern frontend applications, there are several ways to apply styles, each with its own strengths. Choosing the right approach ensures faster development cycles, easier maintenance, and fewer styling regressions. For large-scale Liferay solutions with multiple developers, predictable and conflict-free styles mean less time debugging and more time delivering consistent, maintainable, and high quality features.

Choosing the Right Styling Approach

There are multiple options to choose from when it comes to managing and delivering your branding and styles. For JavaScript applications, there are two choices that have become the de facto options for most solutions.

These two common approaches are SCSS Modules and CSS-in-JS:

  • SCSS Modules: This approach allows you to write maintainable, component-specific styles with the full power of Sass, while automatically generating unique class names to avoid conflicts. This is ideal when you need clear separation between components while still benefiting from variables, nesting, and mix-ins. This approach also offers the benefit of making it much easier to organize and maintain your styles. Best practices dictate that components are often organized into separate files by type. For example, one file that contains all the specification for buttons, another for text, a separate one for spacing, etc. These components are all defined separately, but the bundler will pre-process these files, converting them into CSS equivalents as part of the build process and then merging them all into a single file during optimization. If you need to define the styles once and then reuse them in multiple places, this should be your go-to solution.
  • CSS-in-JS: With this solution, styles are directly integrated into the JavaScript code and often scoped to a single component and applied dynamically at runtime. While this approach does limit the opportunity for reuse, it does provide other benefits. Components become fully encapsulated and deployable, limiting the complexity of interdependencies. This can also provide a highly fluid look-and-feel as it become possible to perform theme switching or applying styles based on the component's state.

The litmus test for deciding between these approaches is to determine the styling scope for your solution.

Global vs. Component-Scoped Styles

Understanding global and component-scoped styles is critical for choosing your styling approach.

  • Using Global Styles: If you have a strong brand requirement and corporate-level assets that can be leveraged to apply the styles, then it makes sense to have them reference a global style.
  • Using Component-Scoped Styles: If your components need to be highly portable so that they can be used in multiple applications (inside and outside of Liferay), then it makes sense to bundle styles with the components themselves.

You do not have to use just one scope for your styles. In many cases, you will use a combination of these two scopes to provide the most flexible solution. But whether you choose just one or a combination of both approaches, it is important to understand the benefits of each and apply them accordingly.

A clear separation of global vs. component-scoped styles keeps branding consistent without stifling creativity at the component level. It also reduces the risk of a small design tweak in one area unintentionally affecting other parts of the site.

Let’s take a look at a real-world example.

Styling Use Cases

Consider these two types of components:

  1. A weather application built with React
  2. A custom form element used to select a city via quick-search

The custom form element isn’t necessarily specific to the weather application. You may also choose to build a separate application (like a distributor locator) where the user has the option to perform a search using this element. In this case, you have a component that is being reused across applications, which makes it a candidate for a UI kit. In this case, any custom styling that this item requires (for example padding between the text field and the list of options) should be part of the component definition itself – this is where we would use CSS-in-JS, meaning the CSS class definitions are part of the component.

import React from 'react';

const MyStyledComponent = () =>  {
  const boxStyle = {
    backgroundColor: '#f0f0f0',
    color: '#333',
    padding: '20px',
    borderRadius: '8px',
    textAlign: 'center',
    fontFamily: 'Arial, sans-serif'
  };

  return (
    <div style={boxStyle}> 
      <h2> Hello, World!>/h2> 
      <p> This is a simple JSX component with inline CSS.</p> 
    </div> 
  );
};

export default MyStyledComponent;

Meanwhile, the weather application might contain multiple components that require common styling. Example styles include text styles, spacing, and other types of layout utilities. There are likely many of these styles that will be defined, so it makes more sense to use the SCSS module approach to simplify maintenance. You would separate the styles by type, and then let the bundler combine them at runtime into a single CSS resource for the weather application.

Now, let’s understand how to handle styling in Liferay specifically.

Handling Styling Options in Liferay

Liferay provides multiple layers for managing CSS styles. What tools and layers you will use depends on the scope you choose.

Liferay provides multiple layers for managing CSS styles.

Theme CSS Client Extension

The theme CSS client extension can provide CSS style sheets, images, and frontend token definitions to modify the theme of a site or page. Liferay’s design component library, Clay, is the foundational framework for implementing custom themes with this client extension. Clay provides a wide selection of custom components, along with a set of CSS variables that you can include in a frontend token definition.

With the theme CSS client extension, you can rewrite Clay variables as needed rather than starting from scratch. By reusing predefined variables, you ensure that all your components adhere to a cohesive design language and responsive layout, enhancing the experience for both the developer and the end user.

For example, instead of creating a custom .card-title style (css class), you can use Liferay’s h4 headings with built-in spacing and font utilities, ensuring consistency with the rest of the site.

Custom CSS Liferay Theme CSS

.card-title {

    margin-bottom: 0.5rem;

    font-size: 1.25rem;

    font-weight: 500;

}

<div class=”card-title”>This is the Title</div>

<div class=”h4 mb-3”>This is the title</div>


You should use Clay as much as possible. Using Liferay’s built-in styles improves time-to-market, ensures brand consistency, and simplifies future updates—especially when upgrading themes or rolling out global brand changes. For situations where the predefined variables are not sufficient, you also have the option of adding a custom CSS file with your own components.

Even with the utility of Clay, overriding the entire theme with this client extension is a costly operation. Unless creating a custom theme is absolutely necessary for your solution, Liferay recommends using the theme CSS client extension to inject a frontend token definition to your instance. With this approach, you can leverage style books to modify site styling without creating additional developer overhead.

Global CSS Client Extension

While themes are a powerful feature in your toolkit, there may be scenarios where you want to implement styling components that do not rely on Clay or style books. In these cases, you should use the global CSS client extension to add CSS resources directly to a page or element. Unlike themes, global CSS is modular and flexible. This client extension specifies only the CSS needed for a given page and no more, so your application avoids loading unused resources. Because of this, unless you need to add a frontend token definition, the global CSS client extension should be your choice for implementing custom styling.

Note that you can use theme CSS and global CSS client extensions together in your solution. The load order is critical: global CSS is loaded after theme CSS. This means that you can implement a theme CSS client extension that governs your overall structure, layout, and styling, but you can override the theme on specific pages with a global CSS client extension. While Liferay recommends the global CSS client extension as your first option, it does not necessarily have to be your only option.

Clarity’s Styling Needs

Clarity has a strong commitment to its brand that goes far beyond just the corporate colors and typography. For Clarity, the scope of branding extends from visuals to user experience and all the way down to content messaging. The decision to invest in programs aimed at expanding their market share and global footprint has made it more important than ever to adhere to the brand. To ensure that everything is consistent, not only across applications but also regions, Clarity has decided to lean into the theme CSS client extension for its solution. Lower-level styling options are not off the table, but should only be explored when theme CSS capabilities impede the business in some ways.

Conclusion

Managing styles in Liferay is a balancing act between establishing global brand consistency and allowing for component-specific flexibility. With the theme CSS client extension, you can create a cohesive, site-wide styling foundation, while the global CSS client extension enables targeted, modular overrides. By strategically combining these approaches, development teams can build visually consistent, maintainable, and scalable frontend solutions.

Next, you’ll apply these concepts to styling Clarity’s distributor solution.

Loading Knowledge