Extending Design Systems with Code

Contact your Skuid representative to access this feature.

While the Design System Studio (DSS) helps streamline many of the common design settings you’ll need for your application, it does not expose every possible style option. Most styling on the web is done through the use of cascading style sheets (CSS), which includes numerous properties. Since Skuid’s DSS does not expose all of these properties, it can leave some style variants unable to match pixel-perfect designs.

For instances like these—where all other design options are exhausted—Skuid allows builders to extend style variants with code. While not recommended for far-reaching and extensive alterations to existing style variants, these extensions can be used to better attain a granular, design-specific look and feel, as well as implement CSS properties that are not available currently within the DSS (such as animations, media queries, and others).

Prerequisites

Warning

Extending with code, while powerful, is complicated. There’s also no support provided for custom CSS, meaning that—while Skuid makes a best effort to retain functionality and document breaking changes—its efforts are focused on its declarative toolkit. It is the builder’s responsibility to ensure their styles function in future updates.

Before starting, ensure you’ve done the following:

  • Exhausted all style options in the DSS
  • Have a working knowledge of the CSS you wish to implement
  • Have a working knowledge of JavaScript, since this is how you’ll be implementing the CSS.
  • Finally, always double check to see if you cannot create your styles declaratively.

Accessing and Editing Style Variant Code

Only component and subcomponent style variants, not variables, can be extended with code. To access the code editor for a style variant within the DSS:

  1. Click the options menu dots-vertical beside the variant.
  2. Click code-block Extend with code.

The user interface will shift, hiding the list of available components to make room for the style extension editor at the bottom of the page.

  • The left pane, marked Default (read-only), displays the component’s available classes and their currently applied styles. This content of this pane is a reference to provide context and cannot be edited. Use this screen to see which classes are available to edit on the style variant you are extending with code. These classes vary from component to component. Classes cannot be added, removed, or modified.
  • The right pane is an editor showing the custom code associated with the current variant. Style code entered in this screen overrides the code in the left pane. Code changes are automatically applied as you type them to the style variant preview at the top of the screen.
  • For some components and subcomponents, there is a file selector at the top of both of these panes. These files represent the various sections of the component, which contain different elements. Because styles from one section do not apply to other sections, you’ll need to navigate these files to style the elements you wish to extend.

You can raise and lower both panes for better visibility of the component preview. The style variant’s declarative property pane also remains on the screen, so it is possible to edit a variant’s properties while extending with code, and see the preview update accordingly.

Note

Skuid uses Monaco—the editor that powers VSCode—as its code editor. Because of this, you have access to an array of features, like multi-line editing and code completion, while using it. For more information, see VSCode documentation.

  1. After extending your style variant, click Done to retain your changes. If you click Cancel, any changes made will be lost.
  2. To complete saving your changes to the design system, you must then click Save.

Once you’ve adjusted a variant using code, the variant is designated with an icon. This badge cannot be removed, even if all custom code is removed.

Exporting and importing design systems will retain your variant extensions.

Writing CSS in JavaScript

When extending style variants with code, you are not writing CSS directly, but instead writing JavasScript that is parsed and compiled into CSS rules. Skuid accomplishes this parsing of styles using JSS, a CSS-in-JS library. For more information and examples of this library’s preferred syntax, see the JSS documentation.

Note

While style variant extensions must be written in JavaScript syntax, this code is only used to generate styles. Once variant styles are compiled based on their code, they are only displayed, not processed, at runtime. Thus, JavaScript logic within variant extensions is not executed at runtime.

Due to this JavaScript-based structure, you cannot copy CSS straight from the devtools of your browser. Instead, CSS properties are written in JSON syntax as key-value pairs. These properties are themselves an object, associated as the value of the CSS class’s key:

1
2
3
4
"button": {
  fontSize: "14px",
  color: "blue"
}

CSS property names can be written as camelCased property names (fontSize) or as strings of literal CSS names ("font-size"). Note that quotation marks denote it as a string.

Finding classes

The Default (read-only) editor displays the classes available for extending, but you’ll need to cross-reference this list with the UI elements on screen to be sure you’re targeting the correct class. This is best done with the devtools of your browser.

To find the classes and elements you’ll need to target, keeping the following in mind:

  • Open the devtools and investigate the elements within the DOM. These class names are not typically displayed at runtime, but they are displayed within this variant extension workspace. Each element will have its proper class name with a prefix similar to c-654321. (This prefix can be ignored.)

  • You cannot apply styles to nested components within variants.

    If, when navigating the DOM, you see attributes like data-te="skuid__buttonSet", that means you’ve navigated to a nested component, and you’ll need to do your styling directly on that component instead. You can then utilize the variant for that nested component within the container component you’re working on.

    • If you find that the class you’re trying to target isn’t working, it could be part of a nested component. For example, targeting a button-group class within a Table component will not work, because the Button Group is a separate component.
  • While it’s possible to target element types, like div, span, or button, it’s best practice to utilize the proper class names within the proper component variants.

  • Additionally, best practice is to avoid highly specific, non-class based selectors. Using targets like div:first-child > span may function, but because they are highly dependent on DOM structure, they are fragile. The DOM structure of a Skuid page is liable to change from release to release.

    Because of that, always try to utilize class-based selectors with minimal use of combinators, such as > or ~.

Skuid’s default code

While extending styles, you’ll likely see some code within properties whose purpose seems unclear. For the most part you can ignore these instances, as you do not need to recreate that logic with your custom code. For example, you do not need to re-implement the instances of Object.assign or r(t.variable) you see within default code.

You also may notice extensive use of t variables, such as t.subtitle.color. These variables represent the settings configured on the style variant within the standard DSS UI, and are not actual values. For example, the t.title.fontSize is just a reference to the style variable set for the Title component’s Font Size, such as Medium or Large. Because of this, it’s not likely you’ll be utilizing these t variable values if extending with code.

Inheritance

Style code extensions are encapsulated within the component (i.e., if you set background, it will extend no further than the component itself). If you have child variants, the child will receive the parent’s CSS and then apply its own.

Common CSS needs

Listed below are some common CSS needs where code may be useful. For additional examples and other CSS features, see JSS syntax documentation.

Pseudo-classes

Pseudo-classes are used to reflect the state of a particular selector. For example:

  • .button:focus would only apply when an element is “focused on,” either through Tab key navigation or a click
  • .button:hover would only apply when hovering over a .button element
  • .button::first-of-type would only apply if the button is the first of its type within a group of elements

As state changes are a common reason to provide unique styling, pseudo-classes can be useful for pixel-perfect style additions. For more information, see MDN documentation.

When utilizing them within style variant code, add the pseudo-class name—prepended with an & ampersand—as a new object within your class object. Then add the CSS rules for that particular pseudo-class nested within the object.

1
2
3
4
5
6
"button": {
    fontSize: "14px",
    "&:hover": {
      background: "blue"
    }
  }

If you find that your styles are not applying, verify that there are no other, more specific pseudo-class selectors overriding your styles. The most specific selector wins, so :hover:not(:disabled) will override :hover.

Media queries

Media queries are useful for handling different screen sizes or for printing. To use them in your custom styles, write the media query as a key, and then write the styles as key-value pairs within an object.

1
2
3
4
5
6
7
8
"button": {
  "width": "100px",
  "font-size": "14px",
  "@media (min-width: 1024px)": {
    "width": "200px",
    "font-size": "20px",
  }
}

One prime use case for media queries is setting up print styles. When printing pages, it can be useful to hide certain elements so as to allow for a cleaner page layout.

1
2
3
"@media print": {
  display: "none"
}

You can also use this in combination with your other styles and media queries.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
"button": {
  "width": "100px",
  "font-size": "14px",
  "@media (min-width: 1024px)": {
    "width": "200px",
    "font-size": "20px",
  },
  "@media print": {
    display: "none"
  }
}

Animations and transitions

CSS provides animation and transition capabilities, but the specificity of their implementation, as well as the complexity of their rules, varies widely by use case. So while you may not see many options for animations in Skuid, you can likely implement them using custom styles with enough know-how.

Note

When writing transition rules, note that transitions can only apply to rules that have a spectrum of values. For example, display cannot transition, because it is a specific setting, but opacity can be used because it can take a spectrum of values.

One important note for working with JSS syntax is that keyframe animations are scoped. Unless you declare them globally, you need to refer to the keyframe rule using a $ character in front:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
"checkbox--checked": {
    "animation-name": "$bounce",
    "animation-timing-function": "ease",
    "animation-duration": "0.5s",
    "animation-iteration-count": "once"
},
"@keyframes bounce": {
    "0%": {transform: "scale(1,1) translateY(0)"},
    "10%": {transform: "scale(1.1,.9) translateY(0)"},
    "30%": {transform: "scale(.9,1.1) translateY(-3px)"},
    "100%": {transform: "scale(1,1) translateY(0)"},
}

Troubleshooting

My styles didn’t apply. [[]]

  • If you find that the class you’re trying to target isn’t working, it could be part of a nested component. Buttons in particular are often nested, so you’ll need to create a variant on the Button component instead of its container component.
  • Check that there are not other, more specific CSS rules being applied. This can often be the case with pseudo-classes. The most specific selector always wins.