How to style and customize Box UI Elements

|
Share
How to style and customize Box UI Elements

ith a recent release, Box has introduced the ability to further customize and apply styling of certain Box UI Elements. Now you can match the look and feel of those components to your custom application. In this article we’ll explore the possibilities with some theming examples and other customization options.

Box UI Elements are the pre-built UI components which allow to add components of the main Box web application into your own project.

Design tokens

First, let’s talk about design tokens. They are used to construct and maintain a design system. The tokens, translated into data, act as the source of truth to ensure that the product experience is cohesive. Design tokens cover design elements like spacing, color, typography, and many others. Tokens enable the ability to support multiple themes like light and dark mode, accessible themes, or for example end user and admin experiences where the “brand” colors may change. Box UI Elements utilize design tokens. You can overwrite values to change appearance of the certain UI features like background colors, borders, animations and more.

Customizing Box UI Elements

If you haven’t used Box UI Elements previously, check out installation guide.

Currently Box Content Explorer and Box Content Uploader can be customized with design tokens. To start styling either of those elements you need to upgrade box-ui-elements package to version 23.3.0 or update CDN links accordingly.

To prevent library duplication, the UI Elements require certain peer dependencies to be installed manually. For a list of required peer dependencies, see package.json.

Styling Content Explorer and Content Uploader

In order to style Content Explorer or Content Uploader you have to pass a theme object that includes tokens. There are two formats you can pass the tokens object. First, token names can be written in a flatten structure using kebab case. Alternatively, you can use a nested structure, just like in the code sample below:

// kebab case format
const themeLight = {
    tokens: {
        "background-background": "white"
    }
};

// nested structure format
const themeDark = {
    tokens: {
        Background: {
            background: "black"
        }
    }
};

const contentExplorer = new Box.ContentExplorer();
contentExplorer.show(configData.FOLDER_ID, configData.ACCESS_TOKEN, {
    container: ".container",
    theme: themeLight,
});

Check out an interactive demo:

CodePen Embed - Box Content Explorer Theming

There is a long list of properties you can customize while customizing Box UI Elements. Let’s dive into some customization options:

Top level tokens

There are multiple top level variables that you might want to overwrite. They mostly relate to radii, spacing, sizing, shadows and more. These accept valid CSS units such as px, rem, em, %.

const tokens = {
    'border-1': '0.0625rem',
    'border-2': '0.125rem',
    'border-3': '0.1875rem',
    'border-4': '0.25rem',
    'border-6': '0.375rem',
    'border-8': '0.5rem',
    'dropshadow-1': '0 0 0.5rem 0 rgba(0, 0, 0, 0.05)',
    'dropshadow-2': '0 0.0625rem 0.25rem 0 rgba(0, 0, 0, 0.1)',
    'dropshadow-3': '0 0.25rem 0.75rem 0 rgba(0, 0, 0, 0.1)',
    'radius-05': '0.125rem',
    'radius-1': '0.25rem',
    'radius-2': '0.375rem',
    'radius-3': '0.5rem',
    'radius-4': '0.75rem',
    'radius-5': '1rem',
    'radius-6': '1.25rem',
    'radius-7': '1.5rem',
    'radius-8': '1.75rem',
    'radius-half': '2rem',
    'size-05': '0.125rem',
    'size-1': '0.25rem',
    'size-2': '0.5rem',
    'size-3': '0.75rem',
    'size-4': '1rem',
    'size-5': '1.25rem',
    'size-6': '1.5rem',
    'size-7': '1.75rem',
    'size-8': '2rem',
    'size-9': '2.25rem',
    'size-10': '2.5rem',
    'size-11': '2.75rem',
    'size-12': '3rem',
    'size-14': '3.5rem',
    'size-15': '3.75rem',
    'size-16': '4rem',
    'size-18': '4.5rem',
    'size-20': '5rem',
    'space-05': '0.125rem',
    'space-1': '0.25rem',
    'space-2': '0.5rem',
    'space-3': '0.75rem',
    'space-4': '1rem',
    'space-5': '1.25rem',
    'space-6': '1.5rem',
    'space-7': '1.75rem',
    'space-8': '2rem',
    'space-9': '2.25rem',
    'space-10': '2.5rem',
    'space-11': '2.75rem',
    'space-12': '3rem',
    'space-14': '3.5rem',
    'space-15': '3.75rem',
    'space-16': '4rem',
    'space-18': '4.5rem',
    'space-20': '5rem',
};

Nested tokens

Besides the top level tokens, there are nested objects with grouped by certain areas like: Border, Icon, Outline, Surface, and Text.

const tokens = {
    // Tokens related to border colors. These accept valid CSS color values.
    Border: {
        'checkbox-border': '#6f6f6f',
        'checkbox-border-hover': '#4e4e4e',
        'checkbox-border-selected': '#0061d5',
        'checkbox-border-selected-hover': '#2079e3',
        'cta-border-outline': '#000000', // secondary buttons w/ transparent background
        'cta-border-outline-disabled': '#646464',
        'cta-border-outline-hover': '#000000',
        'cta-border-outline-pressed': '#000000',
        'cta-border-secondary': '#bcbcbc', // secondary buttons
        'cta-border-secondary-disabled': '#e8e8e8',
        'cta-border-secondary-hover': '#bcbcbc',
        'cta-border-secondary-pressed': '#bcbcbc',
        'divider-border': '#e8e8e8', // horizontal line separators
        'dropdown-border': '#bcbcbc',
        'gridthumbnail-border': '#e8e8e8', // item (file/folder) thumbnails in grid view
        'input-border': '#909090', // text inputs
        'input-border-error': '#ed3757',
        'input-border-focus': '#2486fc',
        'input-border-hover': '#6f6f6f',
        'search-border': '#f4f4f4', // search inputs
        'search-border-hover': '#6f6f6f',
        'switch-border': '#bcbcbc',
        'switch-border-hover': '#bcbcbc',
        'tooltip-border-error': '#f69bab',
    },
    // Tokens related to fill colors used in icon buttons. 
    // These accept valid CSS color values.
    Icon: {
        'cta-icon': '#6f6f6f',
        'cta-icon-hover': '#222222',
        'cta-icon-pressed': '#222222',
    },
    // NOTE: For adjusting files and folder icons, check information
    // in "Other customization options" section of this article
    Outline: {
        'focus-on-dark': '#ffffff',
        'focus-on-light': '#2486fc', // outline color for focused states
    },
    // Tokens related to colors for component surfaces. 
    // These accept valid CSS color values.
    Surface: {
        'checkbox-surface': '#ffffff',
        'checkbox-surface-hover': '#ffffff',
        'checkbox-surface-selected': '#0061d5',
        'checkbox-surface-selected-hover': '#2079e3',
        'cta-surface-icon': 'rgba(0, 0, 0, 0)', // icon buttons
        'cta-surface-icon-disabled': 'rgba(0, 0, 0, 0)',
        'cta-surface-icon-hover': 'rgba(0, 0, 0, 0.04)',
        'cta-surface-icon-pressed': 'rgba(0, 0, 0, 0.08)',
        'cta-surface-outline': 'rgba(0, 0, 0, 0)', // secondary buttons w/ transparent background
        'cta-surface-outline-hover': 'rgba(0, 0, 0, 0.04)',
        'cta-surface-outline-pressed': 'rgba(0, 0, 0, 0.08)',
        'cta-surface-secondary': '#ffffff',
        'cta-surface-secondary-hover': '#f4f4f4',
        'cta-surface-secondary-pressed': '#e8e8e8',
        'cta-surface-tertiary': '#ffffff', // link styled buttons
        'cta-surface-tertiary-hover': '#f4f4f4',
        'cta-surface-tertiary-pressed': '#e8e8e8',
        'dropdown-surface': '#ffffff',
        'dropdown-surface-error': '#ffffff',
        'dropdown-surface-focus': '#ffffff',
        'dropdown-surface-hover': '#ffffff',
        'illustration-surface-box-neutral': '#0061d5', // illustrations (detailed icons)
        'input-surface': '#ffffff', // text inputs
        'input-surface-error': '#ffffff',
        'input-surface-focus': '#ffffff',
        'input-surface-hover': '#ffffff',
        'menu-surface': '#ffffff', // dropdown menu options
        'menu-surface-focus': '#f4f4f4',
        'menu-surface-hover': '#f4f4f4',
        'search-surface': '#f4f4f4', // search inputs
        'search-surface-focused': '#ffffff',
        'search-surface-hover': '#fbfbfb',
        'sliderthumb-surface': '#0061d5', // range sliders
        'sliderthumb-surface-hover': '#2486fc',
        'slidertrack-surface': '#6f6f6f', // range sliders
        'surface': '#ffffff', // general background color
        'surface-brand': '#0061d5', // primary buttons
        'surface-brand-disabled': '#0061d5',
        'surface-brand-hover': '#006ae9',
        'surface-brand-pressed': '#004eac',
        'switch-surface': '#ffffff',
        'switch-surface-off': '#d3d3d3',
        'switch-surface-on': '#0061d5',
        'tooltip-surface': '#4e4e4e',
        'tooltip-surface-error': '#fdebee',
    },
    // Tokens related to text colors. These accept valid CSS color values.
    Text: {
        'cta-link': '#0061d5', // hyperlinks
        'cta-link-disabled': '#b2cff2',
        'cta-link-hover': '#1d6bca',
        'cta-link-pressed': '#2486fc',
        'text-error-on-light': '#d5324e',
        'text-on-dark': '#ffffff',
        'text-on-light': '#222222', // primary texts
        'text-on-light-secondary': '#6f6f6f', // secondary texts
        'text-on-light-secondary-hover': '#4e4e4e',
    }
};

For styling text you might need to add additional CSS rule:

.be.bce.bce * {
    font-family: FONT_NAME;
}

We’ll be including even more tokes for customization in near future. Check our developer documentation for the latest updates and the extensive list of all available design tokens.

Pulling global CSS variables to the token object

Let’s have a simple and quick example of how you can pull your CSS variables into Box UI Elements. Assume that you have some global variables in your CSS file:

body {
   --periwinkle: #D2E1FF
   --primary: var(--periwinkle);
}

You can grab those variables using getPropertyValue method and pass this to your theme object and pass to the token object:

const bodyStyles = window.getComputedStyle(document.body);
const primaryColor = bodyStyles.getPropertyValue('--primary');

const theme = {
    tokens: {
        "background-background": `${primaryColor} !important`
    }
};

const contentExplorer = new Box.ContentExplorer();
contentExplorer.show(configData.FOLDER_ID, configData.ACCESS_TOKEN, {
    container: ".container",
    theme: theme,
});

Other customization options

Box UI Elements allow further customization. Let’s explore possibilities like updating folder and files icons, custom actions, adding custom logo, using events listeners and working with interceptors.

Customizing file & folder icons

In case you want to style or replace files and folder icons with different SVGs, you can do so by targeting specific elements in the DOM. To do so follow the these steps:

  • Identify the aria-label of the icon that you want to style. Use browser developer tools to inspect the DOM and search for the aria-label attribute on the SVG element.
  • Follow the CSS selector pattern below to target the SVG using CSS.
  • Set the fill property of the path element to the desired color. It is helpful to use the browser developer tools to identify the path element that should be updated.

This example shows how to change the color of the Collaborated folder icon:

.be-ItemList-itemIcon[aria-label="Collaborated folder"] {
    path:nth-child(1) {
        fill: pink;
    }
    path:nth-child(2) {
        fill: plum;
    }
}

Check out the following CodePen for extensive examples.

Custom actions

We recently added the possibility to add custom actions to files and folders in Content Explorer and Content Picker Box UI Elements. Now you can easily expand options available in the the menu that shows once user clicks the ellipsis button.

16

To do so, pass an array of custom actions to itemActions.

contentExplorer.show(configData.FOLDER_ID, configData.ACCESS_TOKEN, {
    container: ".container",
    itemActions: customActions,
});

The array can include multiple actions. The action object should include the label , onAction callback function. You can also filter the options to only appear on a specific item type, by passing file or folder value. Finally, filer is used to do more advanced filtering such as if the user wants to filter by specific file extension:

const customActions = [
    {
        label: "Preview in New Window",
        onAction: (item) => alert('action ' + item),
        type: 'file',
    },
    {
        label: "Open in Box.com",
        onAction: (item) => window.open("https://app.box.com"),
    },
    {
        label: "Export",
        onAction: (item) => console.log('action ' + item),
        filter: (item) => item.extension?.toLowerCase() === 'pdf',
    }
];

To see implemented examples check out this CodePen.

Custom logo

Each of the Box UI Elements allow for specifying a custom logo to place in the top-left corner of the embedded container. To use your own logo for example in Content Preview, you need to simply pass the logoUrl.

const preview = new Box.Preview();
preview.show(fileId, accessToken, {
    container: '.preview-container',
    logoUrl: 'URL',
});

Images files will be fitted to a maximum height of 32 pixels and a maximum width of 80 pixels. Larger images will be shrunk to fit these dimensions.

Event listeners and custom actions

In case of customizations Box UI Elements beyond the look and feel there are events you can hook in order to build custom event handlers.

/**
 * Adds an event listener to the content explorer. Listeners should be added
 * before calling show() so no events are missed.
 *
 * @param {string} eventName - Name of the event
 * @param {Function} listener - Callback function
 * @return {void}
 */
contentExplorer.addListener(eventName, listener);

/**
 * Removes an event listener from the content explorer.
 *
 * @param {string} eventName - Name of the event
 * @param {Function} listener - Callback function
 * @return {void}
 */
contentExplorer.removeListener(eventName, listener);

/**
 * Removes all event listeners from the content explorer.
 *
 * @return {void}
 */
contentExplorer.removeAllListeners();

Each Box UI Element has a set of different events, so be sure to check details on our developer documentation site for detailed specification.

Box UI Elements and interceptors

In case of customization edge cases, you can explore working with Box UI Elements and interceptors. Interceptors are part of the Axios library that is used by the Box UI elements to facilitate the communication between the client and the Box API. To learn more, read Peter Christensen's article:

Let’s talk about UI Elements and interceptors

Be sure to follow our Box Developer Blog and change log for updates and stay tuned for upcoming releases.

🦄 Want to engage with other Box Platform champions?

Join our Box Developer Community for support and knowledge sharing!