Carbon: v11 proposed changes for styles

Created on 16 Oct 2019  路  10Comments  路  Source: carbon-design-system/carbon

Disclaimer: this issue is meant for capturing breaking changes that we'd like to see changed in v11 to help address some of the consistency issues across styles. None of these changes are final, or implemented, and are subject to change.

components elements 11

Most helpful comment

Proposal: Utility Classes

Utility classes, as made popular by tachyons or tailwind, would be something really interesting to investigate to see if they make sense in a design system context.

Links & Resources

All 10 comments

Proposal: Sass Modules

The reference implementation for Sass has officially implemented modules. We would love to take the opportunity to update our codebase to leverage this new module system, and specify a direct dependency on a specific sass version (such as dart sass).

Incorporating this proposal will mean that:

  • No longer a need for our import-once mixin
  • No longer a need for us to prefix variables we ship with a carbon-- prefix

Proposal: Module Import Order

We're currently plagued by the fact that any file in our sass source could be imported independently. As a result, we have mechanisms in place for:

  • Toggling whether or not to emit side-effects like a CSS Reset
  • Initializing CSS side-effects like component styles only once

In v11, it'd be great if we specified a specific load order for things like a reset, font face, and other global styles. This would then allow folks to import components independently without having to worry about setting flags around emitting CSS side-effects like a reset.

Proposal: Minimal selector specificity

It is important to be intentional with the specificity of our selectors. If a selector is too greedy or specific, it makes it very difficult developers to override or theme the styles it applies. This results in developers adding extra classes, extra wrappers, or targeting _our_ classes.

Example from our code:

.#{$prefix}--form-item.#{$prefix}--checkbox-wrapper:last-of-type {
  margin-bottom: rem(3px);
}

User's can supply a custom wrapper class to checkbox, however a single class alone is not enough to override the two classes we've used above:

.custom-checkbox-wrapper:last-of-type {
    margin-bottom: 0; // fails
}

div.custom-checkbox-wrapper:last-of-type {
    margin-bottom: 0; // also fails
}

Instead, if we recognized that CheckBoxes only ever exist as form-items, it becomes clear the form-item class is superfluous for this rule. If we eliminate it:

- .#{$prefix}--form-item.#{$prefix}--checkbox-wrapper:last-of-type {
+ .#{$prefix}--checkbox-wrapper:last-of-type {
  margin-bottom: rem(3px);
}

.custom-checkbox-wrapper:last-of-type {
    margin-bottom: 0; // overrides as intended
}

Some simple rules to follow:

  1. Select exactly what you want explicitly. Not more, not less.
    Don't rely on circumstance or coincidence
  2. Do not nest selectors unnecessarily.
    This increases specificity and reduces the flexibility and reusability of your styles
  3. Don not add unnecessary relationships/combinators. same as above.
    Don't use ul > li when ul li will do.

Proposal: CSS-in-JS

Overview

CSS-in-JS has taken a lot of the React community by storm over the past years. The big wins from doing this are things like:

  • Being able to colocate styles and JS for a component
  • Ability to not have to worry about the cascade

    • This is important if order of components loaded is ad-hoc, for example with components A, B, andC` we could load in the order:

    • A -> B -> C

    • B -> A -> C

    • etc

    • All situations above need to work from a styles perspective which can make authoring styles in CSS more difficult, or at the very least is a source of bugs

  • Ability to dead-code eliminate unused styles

    • Often stylesheets on long-lived products are considered "append-only" stylesheets as the cost of changing old styles is amplified and your confidence in making changes to them diminish as the codebase gets larger

And I'm sure that there are other great reasons for this, as well.

However, we often run into challenges with the implementation of CSS-in-JS libraries, which is to say that they prefer being loaded as part of the JS for a product instead of emitting stylesheets to off-load this work. This proposal would aim to blend the best of both strategies where we can get programmatic benefits that help to reduce style size, help to eliminate certain classes of bugs, and overall improves the developer experience while still being performant, fast, and reliable.

Implementation

_Note, a big source of inspiration for this approach comes from Building the new Facebook with React and Relay_

We have the following implementation goals:

  • Amortize the cost of styles over time
  • Make it easy to colocate styles with their components
  • Guarantee that specificity and collisions are non-issues when applying styles. Should be as easy as applying inline styles, and easier for CSS features like media queries that aren't supported in that format by default
  • Make it simple to consume the output of this approach in CSS, Sass, or other targets like Less or Stylus
  • Make it simple to leverage our own strategy to style an entire product
  • Make it simple to still target components with a generic CSS selector, should not rely on generated class names
  • Improve development experience of using and applying styles
  • Add ability to dead-code eliminate styles
  • Add ability to optimize output of styles

Given these goals, the API that we could create may look like:

const styles = stylex.create({
  blue: {
    color: 'blue',
  },
  red: {
    color: 'red',
  },
});

// Generated function can accept Array<keyof T>, where T is object passed to
// stylex.create
styles('blue');

// Can also accept an object with keys of Array<keyof T> and boolean values
styles({
  red: false,
  blue: true,
});

In a React component, this would look like:

// How you would author
const styles = stylex.create({
  default: {
    color: 'red',
    fontSize: 16,
  },
  blue: {
    color: 'blue',
  },
});


function MyComponent(props) {
  const className = styles('default', props.isBlue && 'blue');
  return <span className={className}>Text</span>;
}

// Optimization pass would:
// 1) substitute property values with class names
const styles = stylex.create({
  default: {
    color: 'c0',
    fontSize: 'c1',
  },
  blue: {
    color: 'c2',
  },
});

// 2) Realize that usage can be optimized to be inlined
// this would also drop styles from above and inline class names
function MyComponent(props) {
  const className = 'c1 ' + props.isBlue ? 'c2' : 'c0';
  return <span className={className}>Text</span>;
}

The build step for CSS may emit two types of files:

  • A generated stylesheet with all styles
  • Component-specific styles (e.g. components/accordion.{css,scss}

This would allow everything to work by default but also allow teams to only include the styles that are needed. A natural challenge here is if we're splitting up our stylesheet how do we make sure that things are correctly duplicated across imports that are brought in 馃

Recap

This would be an interesting direction to take the project with tangible benefits for CSS output size and a better development workflow. However, there is risk as this would be an ad-hoc proposal instead of leveraging an existing project. There would also need to be work to figure out how this would play it for how others would consume these styles, or integrate with this to form a complete solution.

Proposal: Utility Classes

Utility classes, as made popular by tachyons or tailwind, would be something really interesting to investigate to see if they make sense in a design system context.

Links & Resources

Proposal: specify Sass dependency

Currently, we haven't specified what we expect teams to be using to compile their Sass. It may be helpful if we changed this to explicitly state the dependency of sass our project uses to prevent confusion moving forward. Most notably, this has come up with the dart sass implementation implementing modules that we are unable to use since we don't specify what sass implementation or version we expect teams to use with our styles.

Proposal: decouple dependencies

We currently combine a number of things in a singular package:

  • Vanilla
  • "Settings" / options
  • Styles

This proposal would be for us to decouple these things into their own packages, alongside shipping additional packages that folks may find useful like feature-flags.

Proposal: use @error to enforce Sass type checking

Context: https://github.com/carbon-design-system/carbon/pull/4931#issuecomment-568062734

In #4931, warnings were added to carbon--rem/rem and carbon--em/rem Sass functions to ensure that only values in pixels are provided as arguments. If the values are unitless (or even the wrong unit) then the output of the functions may be invalid CSS that will break a Sass build.

@warn was used in this context, to minimize breaking changes to users. In v11, it is recommended that the @warn blocks are changed to @error blocks. Using @error blocks here will more effectively enforce the type checking of these Sass functions.

Proposal: improve input background colors in "container components"

In https://github.com/carbon-design-system/carbon/issues/2912 inputs within a modal were reconfigured to utilize background-color: $field-02. It seems this could be applied further to inputs placed within other "container-esque" components that have a $ui-01 background by default. Examples include the Tile and Tabs of type="container".

This was originally raised in https://github.com/carbon-design-system/carbon/issues/4826, PR'ed in https://github.com/carbon-design-system/carbon/pull/5598, and then the changes reverted in https://github.com/carbon-design-system/carbon/pull/5914 due to unintended side effects as Tabs don't currently specify a background color. It seems this should be contained here in a major version bump as it's a potential breaking change for some teams.

Thanks all for weighing in! Going to integrate these suggestions into our roadmap and then out into planning issues 馃憖

Was this page helpful?
0 / 5 - 0 ratings

Related issues

antonmc picture antonmc  路  3Comments

AnthumChris picture AnthumChris  路  3Comments

laurenmrice picture laurenmrice  路  3Comments

ahoyahoy picture ahoyahoy  路  3Comments

snidersd picture snidersd  路  3Comments