Back to Blueprint

Classes

packages/core/src/docs/classes.md

latest7.0 KB
Original Source

@# Classes

Blueprint packages provide React components in JS files and associated styles in a CSS file. Each package exports a Classes constants object in JavaScript that contains keys of the form NAMED_CONSTANT for every CSS class used. This separation allows us to change CSS classes between versions without breaking downstream users (although in practice this happens very rarely).

Avoid referencing hardcoded Blueprint class names in your JS or CSS code.

tsx
// Don't do this! Avoid hardcoding Blueprint class names.
<button className="@ns-button @ns-large">Don't do this!</button>

The best practice is to add your own class to an element and then reference that class whenever needed.

tsx
<Button className="my-custom-class" text="customized button" />
scss
.my-custom-class {
    width: 4000px;
}

In cases where adding and styling a new class is impractical or undesirable, use the Classes constants or $ns Sass/Less variable. The Classes constants can be particularly useful when writing UI tests.

tsx
// use Classes constants for forward-compatible custom elements.
import { Classes } from "@blueprintjs/core";
<a className={Classes.MENU_ITEM}>custom menu item</a>;
scss
// interpolate the $ns variable to generate forward-compatible class names.
// this approach is *not encouraged* as it increases maintenance cost.
@import "@blueprintjs/core/lib/scss/variables";
.#{$ns}-menu-item {
}

@## Modifiers

Blueprint components support a range of modifiers to adjust their appearance. Some commonly used modifiers are intent, large, and minimal. While modifiers are typically implemented as simple CSS classes, it is always preferrable to use the corresponding prop on a React component.

tsx
// Prefer props over modifier classes.
<Button intent="primary" variant="minimal">Good stuff</Button>

// Don't do this!
<Button className={classNames(Classes.INTENT_PRIMARY, Classes.MINIMAL)}>Don't do this!</Button>

Another important note: Since modifiers typically correspond directly to CSS classes, they will often cascade to children and cannot be disabled on descendants. If a <ButtonGroup> is marked variant="outlined", then setting <Button variant="minimal"> on a child will have no effect since Classes.OUTLINED cannot be removed or overriden by a descendant.

@## Namespacing

All Blueprint CSS classes begin with a namespace prefix to isolate our styles from other frameworks: .button is a very common name, but only Blueprint defines .@ns-button.

This CSS namespace is versioned by major version of the library so two major versions of Blueprint can be used together on a single page.

@### Current namespace prefix

Blueprint v6 uses the bp6- prefix for all CSS classes. For example, Button components have the class .bp6-button, Cards have .bp6-card, etc. This prefix changes with each major version of Blueprint (e.g., v5 used bp5-, v6 uses bp6-).

When writing custom styles that target Blueprint components, you should reference the namespace using the $ns Sass variable rather than hardcoding the prefix:

scss
@use "@blueprintjs/core/lib/scss/variables.scss" as bp;

.my-class .#{bp.$ns}-button {
    // Custom styles for Blueprint buttons within .my-class
    background-color: red;
}

@### Custom namespace

The CSS namespace can be changed at build time to produce a custom Blueprint build. With this approach, you will import Blueprint's Sass sources from /lib/scss/ instead of the CSS files from the /lib/css/ folders.

You must use Dart Sass and set up a few important bits of build configuration:

  1. Sass loadPaths must include the node_modules folder where @blueprintjs packages are installed
  2. A custom function implementation for svg-icon() must be defined
  3. You must copy the resources/icons folder from the Blueprint repo into your project (in the future, this may not be required once Blueprint starts publishing these SVG files in a public NPM package).

The @blueprintjs/node-build-scripts package provides some utility functions to help with this. Here's a code example for a custom Sass compiler that can import Blueprint .scss sources:

js
import { sassNodeModulesLoadPaths, sassSvgInlinerFactory } from "@blueprintjs/node-build-scripts";
import * as sass from "sass";

const result = await sass.compileAsync("path/to/input.scss", {
    loadPaths: sassNodeModulesLoadPaths,
    functions: {
        /**
         * Sass function to inline a UI icon svg and change its path color.
         *
         * Usage:
         * svg-icon("16px/icon-name.svg", (path: (fill: $color)) )
         */
        "svg-icon($path, $selectors: null)": sassSvgInlinerFactory("path/to/resources/icons", {
            optimize: true,
            encodingFormat: "uri",
        }),
    },
});

In addition to the JS API, you can specify this configuration with Webpack's sass-loader:

js
// webpack.config.mjs

import { sassNodeModulesLoadPaths, sassSvgInlinerFactory } from "@blueprintjs/node-build-scripts";
import * as sass from "sass";

const functions = {
    /**
     * Sass function to inline a UI icon svg and change its path color.
     *
     * Usage:
     * svg-icon("16px/icon-name.svg", (path: (fill: $color)) )
     */
    "svg-icon($path, $selectors: null)": sassSvgInlinerFactory("path/to/resources/icons", {
        optimize: true,
        encodingFormat: "uri",
    }),
};

export default {
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: {
                    loader: require.resolve("sass-loader"),
                    options: {
                        sassOptions: {
                            loadPaths: sassNodeModulesLoadPaths,
                            functions,
                        },
                    },
                },
            },
        ],
    },
};

Once you have this build configuration set up, you can proceed to customize Sass and JS variables:

  1. Define the $ns Sass variable in your app styles before importing blueprint.scss.
  2. When bundling your JS JS code, define the BLUEPRINT_NAMESPACE variable to the same value; this will update the generated Classes constants. The easiest way to do is with webpack's DefinePlugin.
js
plugins: [
    new webpack.DefinePlugin({
        BLUEPRINT_NAMESPACE: "my-custom-namespace",
    }),
];

@## Linting

The @blueprintjs/eslint-config NPM package provides advanced configuration for ESLint. The @blueprintjs/eslint-plugin NPM package includes a custom blueprint-html-components rule that will warn on usages of JSX intrinsic elements (<h1>) that have a Blueprint alternative (<H1>). See the package's README for usage instructions.