Simple Rules for Styling Hooks

Use these simple rules to help your decision making process when deciding the validity of a styling hook. They are shortcuts to the key aspects of the decision making process and reduce the problem space for Styling Hooks. But they are not stringent; they leave room for nuance and unexpected considerations.

These rules have been created from our learnings about past customer behaviors, Salesforce Design System vision, and technical considerations. The rules will evolve over time as our understanding of the elements that shape the rules evolves. If not, it becomes a dead concept and decisions become arbitrary and scattered.

The Simple Rules

  1. Non-destructive – The CSS property does not introduce the potential for destructive results, security issues, and unfixable accessibility scenarios.
  2. Theming – The CSS property supports component variation through color, typography, and spacing.
  3. SDS Migration – The CSS property and styling hook will not cause migration issues from SLDS to SDS.
  4. Customer Signal – The CSS property should be informed by either past customer behavior or been approved by the SDS working group.

The Simple Rules Expounded

The following is a deep dive into each rule that walks you through the nuances of the decision making process.

Non-destructive

CSS properties with too much freedom to affect other elements on the page are not good candidates for hooks. We want hooks to be predictable and safe when a customer customizes their app with them. If the hook will affect another element unrelated to it on the page, don’t expose the property, this is beyond the customization scope of hooks.

As a rule of thumb, if the property does not fall underneath one of the following categories, it fails the non-destructive rule:

There may be exceptions based on special context. These will need to be evaluated as unique exceptions or the groundwork that may evolve the rules.

Destructive properties also raise security concerns. At this time, we know pointer-events and position have been flagged as security issues in the past.

Theming

Most CSS properties that fall under color, typography, decoration, and spacing are good candidates for receiving styling hooks. These properties are generally safe to customize without being destructive (spacing being the least) and enable a customer’s brand expression in SDS.

Component Level

Properties that are scoped to a component and based on context. i.e. “Within this experience, I want a bigger CTA to capture my audiences attention...”

Shared Level

Note: currently in development and not publicly available

Targeted properties that propagate into more than one component. i.e. “All my form elements share these properties...”

Global Level

Note: currently in development and not publicly available

Properties that holistically change the customer’s application. i.e. “My brand looks like....”

Shared and Global Only

For interconnected elements where consistency is critical, we do not offer component level styling hooks out of the box. Instead, we provide shared or global level styling hooks that perform as levers for consistent, system wide changes. The defaults provided by Styling Hooks should guide designers and developers toward good UX practices. In addition to fallback values, this includes the type of styling hook provided on a property. The inclusion or exclusion of a styling hook level provides information to the user about the opinion of SDS for that property.

The following are the rules for when to use shared and global levels only:

At this time, the following are considered customizable at the shared level only:

It’s important to not be overly paternal and provide flexibility for use cases specific to a sub-system that aren't covered so we provide a path forward for those who need to break the rules for their working context. This path forward creates a distinct separation between what we endorse and do not endorse.

If a customer needs to take this route, they have the following options:

Note: currently in exploration

UI Relationships

When creating a hook, be aware of the related surrounding elements that may have their accessibility affected by a customer’s customization. If this is the case, add a hook to the corresponding element (which should adhere to the simple rules as well) so potential accessibility scenarios are solvable.

For example, if you create a hook on the background color of a component:

--sds-c-foo-color-background

Ensure any elements that share a relationship with it, such as text, are also tied to a hook so both can be changed:

--sds-c-foo-text-color

We cannot mandate the individual values a customer may use but we can provide the flexibility to support their choices in an accessible way.

This rule currently corresponds to:

Kinetics

Styling Hooks related to kinetics is TBD while it is still in early development.

SDS Migration

At this time, we are restricting any hooks added to SLDS to hooks that will exist in SDS. Once SLDS is running on SDS, we may explore hooks that are specific to SLDS. Additionally, customers using SLDS with hooks today who want to migrate to SDS in the near future have a cleaner path forward with the current approach.

Refrain from adding hooks to variants that are specific to SLDS since these will not exist in SDS. If hooks are needed for these variants, they will most likely fall under the slds- namespace when SLDS becomes a sub-system of SDS.

Similarly, do not add individual hooks for instances of components. Instances appear similar to variants at first glance but they differ in nature. Variants have a fundamentally different purpose than the base variant while instances only differ in smaller ways like the content they may contain but their purpose still remains the same. A good example of this is icons.

Lastly, do not name or add hooks that are specific to SLDS as well. Your naming conventions should describe the component’s purpose, not how the classname was written in SLDS. For example, the accordion header is labelled with the class .slds-accordion__summary-heading. Since this is main header for the accordion, we describe it as such in our hook: --sds-c-accordion-heading-*. summary in this context may not apply to other accordion implementations, all we care about is the main heading itself.

Customer Signal

Styling Hooks will evolve based on the usage and feedback of sub-systems built with SDS. Updates that benefit the holistic system will be introduced once sufficient signal from the data has been identied and proper vetting processes have been completed.

Styling Hooks Naming Conventions

Styling hooks use CSS custom properties which make it easy to customize component styling and express your brand. This document covers the anatomy of a hook and the rationale behind each piece.

Anatomy

Styling Hooks is made up by a specific naming convention that promotes predictability, organization, and consistency. All of our CSS custom properties adhere to the following convention:

--[namespace]-[scope]-[context]-[variant]-[element]-[category]-[property]-[attribute]-[state]-[psuedo-state]

Component/Shared Component Level

IdentifierExampleDescriptionRequired
NamespacesdsThe namespace of the system that owns the hook
Scope`cs`The scope defines the reach of your hook
ContextbuttonThe name of the component or feature that is targeted by the hook
VariantbrandA variation of the component
ElementiconThe descendent element of the component
CategorycolorThe category of the property that the hook affects
PropertybackgroundThe semantic UI property being affected
AttributecontrastThe semantic characteristic of a property
StatepressedThe state of a property within context of interaction design
Psuedo StatehoverThe pseudo class that matches a user interaction

At a minimum, a component level style hooks could like like:

--sds-c-button-color-background

Where the most extreme case could be:

--sds-c-button-brand-icon-color-background-contrast-pressed-hover

Global Level

Global level styling hooks have a limited set of variations, plus a range identifier since these are intended for unopinionated use throughout the application.

IdentifierExampleDescriptionRequired
NamespacesdsThe namespace of the system that owns the hook
ScopegThe scope defines the reach of your hook
ElementlinkThe descendent element of the component
CategorycolorThe category of the property that the hook affects
PropertybackgroundThe semantic UI property being affected
AttributeneutralThe semantic characteristic of a property
Psuedo StatehoverThe pseudo class that matches a user interaction
Range1The a numerical indicator that denotes the scale of a property

At a minimum, a global level style hooks could like like:

--sds-g-spacing-1

Where the most extreme case could be:

--sds-g-link-color-background-neutral-hover-1

Implementation Note

If an Identifier is multi-word regardless of space or dash, i.e., button icon or line-height. The identifier should be joined down to a single word, i.e., buttonicon or lineheight.

Namespace

(Required) The namespace of the hook ties it to the system that owns it. For example, the Salesforce Design System (SDS) owns the namespace sds, therefore, any hook that begins with --sds is owned by SDS. Namespacing hooks helps organize our code and prevents name collisions when a system is used within another systems.

How to reserve your namespace

You may request a namespace for your sub-system by following our namespace request process.

/* Namespace Examples */

/* Salesforce Design System (SDS) */
--sds-c-button-text-color

/* Foo Design System (FOO) */
--foo-c-button-text-color

/* Bar Design System (BAR) */
--bar-c-button-text-color

Scope

(Required) The scope defines the level of reach of the styling hook. There are three levels of scope in SDS:

Hooks work from a bottom-up direction, i.e. low-level hooks, such as component-level, will override higher-level hooks, such as globals.

/* Priority Order */

/* ----------------------------------- |
| Global Hooks    -> [Third Priority]  |
| Shared Hooks    -> [Second Priority] |
| Component Hooks -> [First Priority]  |
| ----------------------------------- */

/* Scope Examples */

/* Global - Affects whole application */
--sds-g-text-color

/* Shared - Affects all button components */
--sds-s-button-text-color

/* Component - Affects only base button */
--sds-c-button-text-color

Context

(Optional) A styling hook scoped to a component, e.g. -c-, should only be used by the named component that matches a component specification in SDS.

Using a component-specific styling hook outside the authored component spec is more susceptible to regression and not a supported implementation.

/* Component Examples */

/* Button Component - text-color is only relevant to button */
--sds-c-button-text-color

/* Card Component - text-color is only relevant to card */
--foo-c-card-text-color

Element

*(Optional) A descendent piece of the named component, it is included if the hook needs to target a specific part of the component.

/* Element Examples */

/* Card Header - The header is a descendent element of the card */
--sds-c-card-header-spacing-block

/* Card Header - The footer is a descendent element of the card */
--sds-c-card-footer-spacing-block

Category

(Required) Categories provide groupings for similar classifications of properties and describe the structural makeup of Styling Hooks. This helps keep the system organized and predictable.

CategoryPropertyHook ConventionUsage Description
ColorBackground-color-background-Generic UI background colors
Border-color-border-Generic UI border colors
Link-color-link-Generic UI text link colors
FontStyle-font-style-Supported font family styles
Size-font-size-Generic typographic scale for fonts
Weight-font-weight-Supported font family weights
Line Height-font-lineheight-Relative line-height declarations
Decoration-font-decoration-Appearance of decorative lines on text
Align-font-align-Horizontal alignment of text content
SpacingInline-spacing-inline-Start/End spacing of inline text based on writing mode
Block-spacing-block-Start/End spacing of block text based on writing mode
RadiusRadius-radius-Generic UI radius values
SizingBorder-sizing-border-Generic UI border widths
Square-sizing-square-Generic UI dimensions to output a 1:1 square
Width-sizing-width-Generic UI width
Height-sizing-height-Generic UI height
ShadowShadow-shadow-Generic shadows used for depth
/* Category Examples */

/* Asterisk (*) denotes multiple possible properties can be used */

/* Font Category */
--sds-g-font-*

/* Color Category */
--sds-g-color-*

Property

(Optional) The semantic UI property that the hook is targeting. Excluding the property from a hook makes it a generic UI property.

/* Property Examples */

/* Background Property */
--sds-c-button-color-background

/* Border Property */
--sds-c-button-color-border

Attribute

(Optional) A semantic characteristic of a property.

/* Attribute Example */

/* Global Radius */
--sds-g-radius-border-small

/* Global Spacing */
--sds-g-spacing-medium

State

(Optional) The interaction state that the hook affects.

/* State Examples */

/* Hover State */
--sds-c-button-color-background-hover

/* Focus State */
--sds-c-button-color-border-focus

Directional Support

We aim to align with the flow-relative directional keywords found within the CSS logical properties W3C specification.

/* Horizontal/Inline/X-Axis with location */

--sds-c-[component]-spacing-inline-start
--sds-c-[component]-spacing-inline-end

/* Vertical/Block/Y-Axis with location */

--sds-c-[component]-spacing-block-start
--sds-c-[component]-spacing-block-end

Implicit vs Explicit

A Styling Hook may affect several elements though not plainly expressed (implicit) or affect one plainly expressed element (explicit).

Implicit

An example use-case of an implicit scenario is a global hook that affects many elements, but it is not apparent which elements are affected in the naming convention: --sds-g-color-neutral-base-1. Implicit hooks are handy for "one hook to many properties" implementations.

A hook can be implicit if it passes these rules:

/* Example Implicit Hooks */

/* Global Level */
--sds-g-radius-border-medium

/* Component Level */
--sds-c-button-radius-border

Explicit

Their precise nature quickly identifies explicit hooks. Explicit hooks are useful for scoping the reach of the hook to specific parts of a component.

A hook can be explicit if it passes these rules:

/* Example Explicit Hooks */

/* Explicit hook in accordion */
--sds-c-accordion-summary-color-background

/* Explicit hook in card */
--sds-c-card-footer-spacing-block-start