DRAFT

Overview

Paragon, the design system and React UI component library for the Open edX platform, now supports design tokens and CSS variables.

The design tokens are currently published under an alpha distribution channel on NPM, i.e. @openedx/paragon@alpha.

Getting Started

TODO: docs/resources/context

Some potential ideas:

Migration guide

Requirements:

In case of conflicts with dependency versions, a temporary solution is to install modules using the npm install --force command.

It should also be remembered that some components with the deprecated marker were removed in the alpha version of Paragon. Errors with imports are expected. In the future, these deprecated components will be replaced with new ones (See - Removal-previous-deprecated-components)

  1. Install a compatible version of Paragon. Must support design tokens (currently, only @openedx/paragon@alpha).

    1. https://github.com/openedx/paragon/tree/alpha.

      Example (package.json):
      "@openedx/paragon": "22.0.0-alpha.25"

  2. Install a compatible version of @edx/frontend-build

    1. Requires https://github.com/openedx/frontend-build/pull/365

      Example (package.json):
      "@openedx/frontend-build": "github:edunext/frontend-build#dcoa/design-tokens-support"

  3. Install a compatible version of @edx/frontend-platform.

    1. Requires https://github.com/openedx/frontend-platform/pull/689
      Example (module.config.js):

module.exports = {
    localModules: [
        { moduleName: '@edx/frontend-platform', dir: '../frontend-platform', dist: 'src' }
    ],
};

For Tutor add a mount in the next format:

tutor mounts add <MFE>:<local-path>:/openedx/<npm-package-name>
  1. Install a compatible version of @edx/frontend-component-header (version/PR TBD).

    1. OpenEdx Frontend-component-header - https://github.com/openedx/frontend-component-header/pull/351
      OR

    2. Edx Frontend-component-header - https://github.com/edx/frontend-component-header-edx/pull/393

      Example (module.config.js):

module.exports = {
    localModules: [
        { moduleName: '@edx/frontend-platform', dir: '../frontend-platform', dist: 'src' },
        { moduleName: '@edx/frontend-component-header/dist', dir: '../frontend-component-header', dist: 'dist' }
    ],
};

For Tutor add a mount in the next format:

tutor mounts add <MFE>:<local-path>:/openedx/<npm-package-name>
  1. Install a compatible version of @edx/frontend-component-footer (version/PR TBD).

    1. OpenEdx Frontend-component-footer - https://github.com/openedx/frontend-component-footer/pull/303
      OR

    2. Edx Frontend-component-footer - https://github.com/edx/frontend-component-footer-edx/pull/294 .

      Example (module.config.js):

module.exports = {
    localModules: [
        { moduleName: '@edx/frontend-platform', dir: '../frontend-platform', dist: 'src' },
        { moduleName: '@edx/frontend-component-header/dist', dir: '../frontend-component-header', dist: 'dist' },
        { moduleName: '@edx/frontend-component-footer/dist', dir: '../frontend-component-footer', dist: 'dist' }
    ],
};

For Tutor add a mount in the next format:

tutor mounts add <MFE>:<local-path>:/openedx/<npm-package-name>
  1. [Optional] Install a compatible version of the Brand. Must support design tokens (currently, only @edx/brand-edx.org@alpha).

    1. https://github.com/edx/brand-edx.org/tree/alpha

      Example (package.json):
      "@edx/brand": "npm:@edx/brand-edx.org@2.2.0-alpha.17"

OR

module.exports = {
    localModules: [
        { moduleName: '@edx/frontend-platform', dir: '../frontend-platform', dist: 'src' },
        { moduleName: '@edx/frontend-component-header/dist', dir: '../frontend-component-header', dist: 'dist' },
        { moduleName: '@edx/frontend-component-footer/dist', dir: '../frontend-component-footer', dist: 'dist' },
        { moduleName: '@edx/brand', dir: '../brand-edx.org/' }
    ],
};
  1. Replace @edx/paragon with @openedx/paragon

paragon migrate-to-openedx-scope .
  1. Replace SCSS variables usages or definitions with CSS variables

paragon replace-variables -p . -t usage
paragon replace-variables -p . -t definition

Using locally installed Paragon CSS

  1. Import the compiled Paragon CSS into the SCSS entry point of your MFE.

    1. // Within `src/index.scss` of your MFE
      @use "@openedx/paragon/dist/core.min.css" as paragonCore;
      @use "@openedx/paragon/dist/light.min.css" as paragonLight;
      
      // If using `@edx/brand` to customize the default Paragon theme, also include:
      @use "@edx/brand/dist/core.min.css" as brandCore;
      @use "@edx/brand/dist/light.min.css" as brandLight;
      
      // include the remaining SCSS for the MFE, now with access to the
      // CSS variables defined in Paragon.
note

I believe it may still be an open question on how consumers relying on locally installed Paragon CSS in this manner will be able to take advantage of a future @openedx/paragon/dist/dark.min.css to account for theme switching.

@import "@openedx/paragon/dist/light.min.css" (prefers-color-scheme: light);

@import url("@openedx/paragon/dist/dark.min.css") (prefers-color-scheme: dark);

Using the externally hosted Paragon CSS via @edx/frontend-platform (see below) comes with a mechanism, i.e. const { paragonTheme: { setThemeVariant } } = useContext(AppContext);, to build a theme variant switcher baked in.

I believe it may still be an open question on how consumers relying on locally installed Paragon CSS in this manner will be able to take advantage of a future @openedx/paragon/dist/dark.min.css to account for theme switching.

@import "@openedx/paragon/dist/light.min.css" (prefers-color-scheme: light);

@import url("@openedx/paragon/dist/dark.min.css") (prefers-color-scheme: dark);

Using the externally hosted Paragon CSS via @edx/frontend-platform (see below) comes with a mechanism, i.e. const { paragonTheme: { setThemeVariant } } = useContext(AppContext);, to build a theme variant switcher baked in.

Using externally hosted Paragon CSS

  1. Configure external Paragon CSS URLs (e.g., from a hosted CDN).

    1. The quickest way to get started is to use jsDelivr, a global production-ready CDN that has deep integrations with NPM, and a $paragonVersion wildcard to rely on the same version of @openedx/paragon installed in your MFE. Configure the PARAGON_THEME_URLS in your MFE configuration (you can use either of the approaches explained below).
      Example:

      1. {
            "MFE_CONFIG_OVERRIDES": {
                "<APP_ID>": {
                    "PARAGON_THEME_URLS": {
                        "core": {
                            "urls": {
                                "default": "https://cdn.jsdelivr.net/npm/@openedx/paragon@$paragonVersion/dist/core.min.css"
                            }
                        },
                        "defaults": {
                            "light": "light",
                            "dark": "dark"
                        },
                        "variants": {
                            "light": {
                                "urls": {
                                    "default": "https://cdn.jsdelivr.net/npm/@openedx/paragon@$paragonVersion/dist/light.min.css"
                                }
                            },
                            "dark": {
                                "urls": {
                                    "default": "https://cdn.jsdelivr.net/npm/@openedx/paragon@$paragonVersion/dist/dark.min.css"
                                }
                            }
                        }
                    }
                }
            }
        }

    2. Configuration approaches:

      1. JavaScript-based configuration

      2. MFE runtime configuration API

  2. Import custom media breakpoints from Paragon into the SCSS entry point of your MFE.

    1. // Within `src/index.scss` of your MFE
      @use "@openedx/paragon/styles/css/core/custom-media-breakpoints.css" as paragonCustomMediaBreakpoints;
      
      // If using `@edx/brand` to customize the default Paragon theme, also include:
      @use "@edx/brand/paragon/css/core/custom-media-breakpoints.css" as brandCustomMediaBreakpoints;
    2. Note: you must utilize @use instead of @import with this file. The SASS team also discourages the continued use of @import, and plans to eventually remove it from the language entirely. See more information here.

Accounting for breaking changes

The design tokens release of Paragon is a complete re-architecting of Paragon’s styles system to go from sole reliance on SCSS variables to adopting an industry standard of design tokens and CSS variables. Such a large change means there are breaking changes along the way, though we’ve tried to minimize impact during the migration.

Remove all (almost) Paragon imports from the SCSS entry point in your MFE

Previously, to use Paragon’s styles in your MFE, you’d import various SCSS files from @edx/paragon in a particular order and your MFE’s build process would compile and bundle Paragon’s SCSS with your application’s CSS:

// src/index.scss
@import "@my-brand/fonts.scss";
@import "@my-brand/variables.scss";

// Import the Paragon core after setting brand-themed variables.
@import "@openedx/paragon/scss/core/core.scss";

// Import brand overrides after the Paragon core.
@import "@my-brand/overrides.scss";

This approach had a few negative effects:

  1. Each MFE had to re-compile Paragon’s SCSS whenever a theme change was made.

  2. Users would need to re-download duplicate CSS from Paragon as they navigate across MFEs.

By externalizing the build process for Paragon’s SCSS, consuming MFEs can simply rely on CSS that’s already been compiled and hosted elsewhere, reducing build times and improving page performance for users by requiring less data to download over the network.

The only import from Paragon needed now is for its custom-media-breakpoints. This must be imported within your MFE in order to use the CSS media queries using Paragon’s defined breakpoints.

For example:

// Within `src/index.scss` of your MFE
@use "@openedx/paragon/styles/css/core/custom-media-breakpoints.css" as paragonCustomMediaBreakpoints;
@use "@openedx/brand/paragon/css/core/custom-media-breakpoints.css" as brandCustomMediaBreakpoints;

@media(--min-pgn-size-breakpoint-md) {
  .my-element {
    color: red;
  }
}

Replace all SCSS variables/mixins from Paragon with their CSS variables replacement

There should be a drop-in replacement design token and CSS variable for every previous SCSS variable available via Paragon. For example:

$primary-500 -> var(--pgn-color-primary-500)

Some SCSS mixins are no longer available, so your styles may need to be refactored such that they utilize Paragon’s new CSS variables instead of the previous SCSS mixin.

Removal of previous deprecated components

There is a mapping of Deprecated -> Relevant component:

CheckBox --> Form.Checkbox
CheckBoxGroup --> Form.CheckboxSet
RadioButtonGroup --> From.RadioSet
Fieldset --> cannot be fully replaced, docs say that this component is not needed anymore. Instead just use Form.Group, Form.Row and Form.Col to group form fields
Input, InputSelect, InputText --> Form.Control
ListBox --> SelectableBox
Modal --> ModalDialog
StatusAlert --> Alert
Table --> DataTable
ValidationFormGroup --> Form.Group

FontAwesome removal from Paragon

Removed from components:


Paragon CLI Documentation

Paragon CLI is a powerful tool that provides various commands for working with design tokens, themes, and SCSS files. Below is a comprehensive guide on how to use the available commands.

Available Commands:

  1. install-theme

This command is used to install a specific @edx/brand package. You can specify the theme to install using the theme parameter. By default, the latest version of @openedx/brand-openedx will be installed.

Example:

paragon install-theme --theme @custom/brand-theme@latest
  1. migrate-to-openedx-scope

This command is used for migrating from "@edx/paragon" to "@openedx/paragon":

Example:

paragon migrate-to-openedx-scope .
  1. build-tokens

This command is used to build Paragon design tokens. You can customize the token build process using various options:

Example:

paragon build-tokens --build-dir ./styles/css
  1. replace-variables

This command allows you to replace the SCSS variable's usages or definitions with CSS variables and vice versa in .scss files. You can specify the file or directory where the replacements should be made using the filePath parameter. Additional options include:

Example:

paragon replace-variables -p src -t usage

paragon replace-variables -p src -t definition
  1. build-scss

This command compiles Paragon's core and themes SCSS into CSS. You can customize the compilation process using various options:

Example:

paragon build-scss --corePath ./styles/scss/core/core.scss --themesPath ./styles/css/themes --outDir ./dist/css --defaultThemeVariants light
  1. help

You can display help for any specific command by using the help command followed by the command name.

Example:

paragon help

paragon help build-tokens

Start your MFE application

  1. Load up your MFE on your machine with npm start and open it in a browser. Your MFE should start, with identical styles as before prior to the upgrade.

  2. Verify the styles (e.g., for a Button) in the devtools are now showing as using CSS variables instead of hardcoded values.

  3. If you notice any style issues or discrepancies between the previous look and feel, please file a Github issue and/or reach out in the Paragon Working Group Slack channel.

    1. Work with your squad’s designer(s) to ensure no style regressions.