DevExpress / DevExtreme

HTML5 JavaScript Component Suite for Responsive Web Development
https://js.devexpress.com/
Other
1.8k stars 594 forks source link

DevExtreme Style Sheets - Migration to Sass #13654

Closed babich-a closed 2 years ago

babich-a commented 4 years ago

The Problem

In 2020, many front-end developers use style sheet languages with more features - such as Sass and Less, and no longer utilize pure CSS. Sass (specifically, the more recent SCSS syntax of Sass) has gained popularity and overtaken Less. Both Bootstrap and Material now use Sass. Angular CLI, Vue CLI, and Create React App also support Sass out-of-the-box, and Webpack includes a Sass loader.

DevExtreme style sheets are written in Less. Unfortunately, Less and Sass style sheets are not compatible with each other. The result is that our Less style sheets cannot be integrated into a project that uses Sass.

The Proposed Solution

Based on the popularity of Sass, we plan to migrate our style sheets to SCSS syntax. You will be able to import and set widget styles in your SCSS files. For instance, if you wish to apply our Material Blue Light Compact theme and use only DevExtreme Accordion and Button components in your app, the SCSS configuration would appear as follows:

// set theme settings
@use "node_modules/devextreme/scss/widgets/material/colors" with (
    $color: "blue",
    $mode: "light"
);
@use "node_modules/devextreme/scss/widgets/material/sizes" with ($size: "compact");

// widgets used by app
@use "node_modules/devextreme/scss/widgets/material/accordion";
@use "node_modules/devextreme/scss/widgets/material/button";

Our SCSS style sheets are modular, and they require Dart Sass compiler v1.23.0 or later. If your application does not yet use modules, refer to the following article for migration details: Sass: The Module System is Launched.

Although our Less styles were not declared as public API (and were not documented), some developers may have used them to customize CSS in their projects. Please let us know in the comments below if you used them. We will come up with a migration plan just for you.

We Need Your Feedback

Take a Quick Poll

How does the new Feature meet your needs?

Which style sheet language did you use in your last application?

Get Update Notifications

Subscribe to this thread, or to our Facebook and Twitter accounts, for updates on this topic.

sapelson commented 4 years ago

A "theme" is not just a color. Please also consider using configurable variables for

  1. Typography (font faces, sizes, weight, leading)
  2. Spacing (8px grid is a very common convention, so using multiples of 8 would be great)
  3. Shadows
  4. Border radiuses

So, customisable colors with predefined size sets ($size: "compact") would be a great first step, but a first step only. An app might be using themes on its on, even if just dark and light.

Also, a full theme always means consistent animations (speed, and character) and icons across app. So, if an app uses one component from DevExpress (datagrid, for example), it could be, ideally, fully customised to feel like an integral part of the rest of the app.

babich-a commented 4 years ago

@sapelson, scss files - are our sources, not an API. To configure scss, you can change any variable (see https://github.com/DevExpress/DevExtreme/blob/20_2/scss/widgets/material/_colors.scss#L6 where variables used by the entire theme are defined). $color/$mode variables are used to set the color scheme and they are mandatory. Please note that we can change source files at any time.

httpete commented 4 years ago

I have been proposing this to DX for years I am very happy to see @babich-a taking it up. Thank you. Our app is a Bootstrap 4 app, and we have overridden many of the BS4 $scss variables. This is the cheapest and easiest way to customize a whole set of BS components that utilize those bootstrap classes. I want the exact same thing for DX. Currently, I had to use the theme exporter site and then hack / convert to scss. I only want to import the dx/core scss sheets that I need. For example the scheduler thing is huge, we don't use it, and bloats our css terribly. I have never seen this @use syntax before, Bootstrap just does !default on their variables so if you define (as we do) _custom_theme_vars.scss it will win and override the downstream one.

My other pain with dx is the extremely deep and bloated html that is hard to override and customize. There is a ton of overlap and duplication of css utilities in DX which also adds bloat. I really wish that dx would just let me configure all of it to use bootstrap 4 utility classes. Having DX bring their own for this is a lot. I could see a config that lets me choose between say Material and Bootstrap (tailwind is another) and just have DX use those utility classes for basic positioning and layout.

sapelson commented 4 years ago

I think, asking Devexpress to use Bootstrap classnames is a bit of a stretch :P.

But Bootstrap-style configuration file that lists all available variables would be awesome.

httpete commented 4 years ago

The ultimate would be to be able to configure the utility class library, if you want to bring your own, falling back to DX's. All utility libraries pretty much do the same thing for example d-block is display: block in Bootstrapese.

But the essential part is devextreme's theme variables distilled as bootstrap 4's like this: https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss

and then importing each component's sheet selectively.

miro-ur commented 4 years ago

Would anyone be able to provide example of a setup in Angular ? I was not able to set up SCSS dx styles with and Angular 9 app. The @use does not seem to be working but (no error just ignored) - I can't find a working setup example anywhere. Any ideas on how to make this work with Angular app would be much appreciated. Thanks in advance.

babich-a commented 4 years ago

Hi, @sapelson, @httpete.

According to the new sass module system, you need to configure each module separately. The @import directive, that allows defining all variables in one file and use it in another file will be deprecated before October 1, 2021 and then dropped.

(The following examples will be shown for the material theme)

You can find base variables for the DevExtreme theme in the node_modules/devextreme/scss/widgets/material/colors module. Color constants for a widget can be found in the widget modules (for example, node_modules/devextreme/scss/widgets/material/button/colors).

I have prepared a simple example to show how a theme can be configured.

First of all, install sass, a pre-release version of devextreme and jquery (to simplify code running):

npm i sass devextreme@20.2.1-alpha-20195-1008 jquery

Create the index.html file with the following content:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="./node_modules/devextreme/dist/css/dx.common.css"/>
    <link rel="stylesheet" type="text/css" href="./test.css"/>
    <script type="text/javascript" src="./node_modules/jquery/dist/jquery.js"></script>
    <script type="text/javascript" src="./node_modules/devextreme/dist/js/dx.all.debug.js" charset="utf-8"></script>
</head>
<body class="dx-viewport">
    <div id="button"></div>
    <script>
        $(function() {
            $("#button").dxButton({
                text: 'Click me!',
                icon: "home"
            });
        });
    </script>
</body>
</html>

Then create the test.scss file with the following content:

@use "node_modules/devextreme/scss/widgets/material/colors" as A with (
    $color: "blue",
    $mode: "light",

    // base variables (affects entire theme and all widgets)
    $base-text-color: green,
    $base-bg: gray
);

@use "node_modules/devextreme/scss/widgets/material/sizes" with ($size: "compact");

@use "node_modules/devextreme/scss/widgets/material/button/colors"  as B with (
    // colors, specific for button
    $button-normal-icon-color: blue
);

// typography settings (fonts, background color)
@use "node_modules/devextreme/scss/widgets/material/typography";

// widgets used by app

// my button has an icon, so I need icon classes
@use "node_modules/devextreme/scss/widgets/material/icons";
// button
@use "node_modules/devextreme/scss/widgets/material/button";

Compile the theme and copy icons.

npx sass test.scss test.css
cp -r node_modules/devextreme/dist/css/icons .

Open index.html. You'll see a page with a gray background and a button with green text and a blue icon.

@miro-ur, angular docs states that you can use scss instead of css. Make sure that you use the dart-sass compiler (that allows using the @use directive), not node-sass. Starting with 8.0, angular-cli uses it by default.

To start a new project with Sass, use the following command

ng new my-scss-app --style=scss

To migrate the existing project, you can follow this guide.

httpete commented 4 years ago

I dont see how this will work what you are proposing. I don't have to do any compilation to css, I just import my bootstrap 4 scss sheet and angular cli does the internal compiling. An example stackblitz that showcases using angular-cli to compile devextreme scss would be very helpful.

babich-a commented 4 years ago

Hi, @httpete . I have prepared a simple project that shows how SCSS can be used. Take a look this repository. It has two branches. master shows how to import full bundle ( https://github.com/babich-a/angular-scss-example/blob/master/angular.json#L28), and master-configurable shows how to import separate widgets (https://github.com/babich-a/angular-scss-example/blob/master-configurable/src/styles.scss).

hakimio commented 4 years ago

@babich-a how will this work when using devextreme-cli?

babich-a commented 4 years ago

@hakimio repository in this reply is an angular-cli project.

hakimio commented 4 years ago

@hakimio repository in this reply is an angular-cli project.

So, you won't be using devextreme-cli to generate style bundles anymore, but I still have to manually import styles for each component I am using in main styles.scss file?

babich-a commented 4 years ago

@hakimio repository in this reply is an angular-cli project.

So, you won't be using devextreme-cli to generate style bundles anymore, but I still have to manually import styles for each component I am using in main styles.scss file?

Sorry, @hakimio. You asked about devextreme-cli, not angular-cli. You can use devextreme-cli to generate styles like before.

hakimio commented 4 years ago

You can use devextreme-cli to generate styles like before.

Would be nice if it could generate scss bundle instead of just pre-processed css.

babich-a commented 4 years ago

You can use devextreme-cli to generate styles like before.

Would be nice if it could generate scss bundle instead of just pre-processed css.

What do you mean by 'scss bundle'? DevExtreme scss with changed variables?

hakimio commented 4 years ago

What do you mean by 'scss bundle'? DevExtreme scss with changed variables?

I was thinking about two files:

babich-a commented 4 years ago

I was thinking about two files:

  • One SCSS file with all the variables from all the widgets specified in DX metadata.json file, so that I could easily change them and use them in my own SCSS style-sheets. Auto-generated variables.scss file right now only contains 5 main variables.

  • One with the actual style-sheets for all the widgets specified in metadata.json. It could either be module imports like the ones you showed or just all widget SCSS inlined in a single file. SCSS bundles provided by DX right now, include all the widgets, no matter if you are using them or not.

We will consider SCSS archive importing with the ThemeBuilder (or the number of files with devextreme-cli). At present you can change scss/widgets/generic/_index.scss to exclude some widgets and scss/widgets/generic/_variables.scss to change variables.

calabo-Act21 commented 3 years ago

I was able to use this new feature successfully by creating the following scss file, and include it in my angular.json file :

$dx-accent-color: #9c27b0;
@use "node_modules/devextreme/scss/widgets/material/colors" with (
    $color: "blue",
    $mode: "light",
    $base-accent: $dx-accent-color
);

@use "node_modules/devextreme/scss/widgets/material/sizes" with ($size: "default");
@use "node_modules/devextreme/scss/widgets/material/typography";

// widgets used by app
@use "node_modules/devextreme/scss/widgets/material/icons";
@use "node_modules/devextreme/scss/widgets/material/button";
@use "node_modules/devextreme/scss/widgets/material/dataGrid";

But I am not able to use a CSS variable to define $dx-accent-color value. If I write this :

$dx-accent-color: var(--theme-secondary-500);

I get a Error : "SassError: $color: var(--theme-secondary-500) is not a color."

I know it is not a devextreme specific issue, more a @use configuration limitation, but I'm not able to find a workaround to that. More generally, I don't really know how can I use a color from my material palette or css variable to set my $base-accent color value, and not a static color value.

calabo-Act21 commented 3 years ago

(maybe I should juste create my own version of node_modules/devextreme/scss/widgets/material/colors file, and import it with @use ... ?)

babich-a commented 3 years ago

Hi @dh-fallen. Thank you for your feedback. It is not a full Sass-relative issue. We use some variables to calculate other colors (eg. https://github.com/DevExpress/DevExtreme/blob/20_2/scss/widgets/generic/gridBase/_colors.scss#L208). Sass requires the first argument of color.change, lighten, darken and other functions to be a color and can't use css variables as values (it should know a color value at compile time).

calabo-Act21 commented 3 years ago

Hi @dh-fallen. Thank you for your feedback. It is not a full Sass-relative issue. We use some variables to calculate other colors (eg. https://github.com/DevExpress/DevExtreme/blob/20_2/scss/widgets/generic/gridBase/_colors.scss#L208). Sass requires the first argument of color.change, lighten, darken and other functions to be a color and can't use css variables as values (it should know a color value at compile time).

Yeah that is what I am realizing since yesterday : my problem is a very common matter, and we cannot use a runtime css variable in a precompiled Sass color function ... What a pity, since I have defined angular material palettes in my app using css variables, and it's working great.

The DX SASS feature is great, but we should have some way to add theming capabilities to the styling approach : how can I change the accent color depending on my client ?

babich-a commented 3 years ago

@dh-fallen There is no way to change the DevExtreme accent color at runtime. You can change only the entire theme.

httpete commented 3 years ago

@babich-a devextreme should support either way of variables - scss or css3.

bzupancic commented 3 years ago

@babich-a I'm struggling to modify sizes and would love any assistance or further understanding you can provide.

Building on the example you shared above, I am attempting to adjust the button padding. @use 'node_modules/devextreme/scss/widgets/generic/colors' with ( $color: 'light' ); @use 'node_modules/devextreme/scss/widgets/generic/sizes' with ( $size: 'default' ); @use 'node_modules/devextreme/scss/widgets/generic/button' with ( $generic-button-horizontal-padding: 10px);

This configuration doesn't work. Even though $generic-button-horizontal-padding is declared using !default, I get this error: SassError: This variable was not declared with !default in the @used module.

Should this variable be available? If I am simply going about this the wrong way, any guidance you can provide would be greatly appreciated.

babich-a commented 3 years ago

Hi @bzupancic. The scss/widgets/generic/button module does not have a $generic-button-horizontal-padding variable. The scss/widgets/generic/button/sizes module contains it.

To configure this variable, you need to modify your code as follows:

// sizes namespace already declared so we need buttonSizes namespace 
@use "../node_modules/devextreme/scss/widgets/generic/button/sizes" as buttonSizes with ($generic-button-horizontal-padding: 10px);
@use "../node_modules/devextreme/scss/widgets/generic/button";

It will compile but will not work. Unfortunately, it is impossible to configure sizes now. This variable will be rewritten in this line. We have plans to allow customization of size variables. So, I created a ticket in our support center. You can subscribe to it so that you receive a notification when it is updated.

BrandoCaserotti commented 3 years ago

Would be nice if you could add more variables to improve widgets / overall theme customization. Currently every widget (generic and material) own two scss files (_color.scss and _sizes.scss) but many of them are just empty.

mGloerfeld commented 3 years ago

After working on the SCSS for a while, I noticed some problems. Especially since we use webpack 5 with dart-sass and bootstrap (also v5) in our projects.

For example, this code cannot be compiled via webpack

@use "node_modules/devextreme/scss/widgets/material/colors" as A with (
    $color: "blue",
    $mode: "light",

    // base variables (affects entire theme and all widgets)
    $base-text-color: green,
    $base-bg: gray
);

@use "node_modules/devextreme/scss/widgets/material/sizes" with ($size: "compact");

@use "node_modules/devextreme/scss/widgets/material/button/colors"  as B with (
    // colors, specific for button
    $button-normal-icon-color: blue
);

// typography settings (fonts, background color)
@use "node_modules/devextreme/scss/widgets/material/typography";

// widgets used by app

// my button has an icon, so I need icon classes
@use "node_modules/devextreme/scss/widgets/material/icons";
// button
@use "node_modules/devextreme/scss/widgets/material/button";

Error: Can't resolve 'fonts/Roboto-300.woff' in 'C:\U...

I think WebPack cannot use relative paths.

In general we still have problems with:

So notes out of our projects We need 1 theme and color setup per project. The reason is that we normaly work ontop of an corporate identity (design). Switching between material generic and accent colors is nice but not realy interesting or necessary. In the most cases we need per project only 2, 3 or maybe 4 components. So it is realy necessary to get only thoses components into a bundle and compose this with some other frameworks. In the most cases we need to support repsonsive design including mediaquerys and diffrent devices with dpi values. so it is difficult to work with "px" values.

Still, we think it's a good idea to use scss and @use technology. however, it is currently very difficult to use the Devexpress SCSS files in real world projects.

babich-a commented 3 years ago

Hi, To resolve the issue with Error: Can't resolve 'fonts/Roboto-300.woff' in 'C:\U..., you need to use the https://www.npmjs.com/package/resolve-url-loader plugin.

mGloerfeld commented 3 years ago

note for all guys using webpack:

Webpack v4 : resolve-url-loader Webpack v5 : asset-modules

to solve some issues.

ksercs commented 2 years ago

Thank you everybody, who gave us feedback on this feature. I'm closing this thread. In the case of bugs or questions, feel free to create a new ticket in our Support Center.