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
- Non-destructive – The CSS property does not introduce the potential for destructive results, security issues, and unfixable accessibility scenarios.
- Theming – The CSS property supports component variation through color, typography, and spacing.
- SDS Migration – The CSS property and styling hook will not cause migration issues from SLDS to SDS.
- 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
- Affects another element that is unrelated to the styling hook
- Triggers security flags
- Creates an unfixable accessibility issue
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:
- Color
- Typography
- Spacing
- CSS properties that fall outside the 'inner' box-model, such as
margin, are considered destructive.
- CSS properties that fall outside the 'inner' box-model, such as
- Decorative
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
- Enables a customer to express their brand through color, typography, decoration, and spacing.
- Qualifiers of this rule do not automatically receive a component, shared, and global level hook.
- Respect the integrity of elements that are tightly interconnected at a system-wide level.
- Do not expose hooks that create irremediable accessibility issues.
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...”
- one property to one property — targeted element, property only for that element
- one property to many elements — targeted elements with shared properties inside the boundaries of the customer authored component
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...”
- one property to many components — e.g., input backgrounds, heading font sizes, etc.
Global Level
Note: currently in development and not publicly available
Properties that holistically change the customer’s application. i.e. “My brand looks like....”
- one property to one property — can only be used on a single property. e.g., font-families, inherited properties
- one property to many properties — can be shared across properties. e.g., brand color, border colors, etc.
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:
- Communicates important feedback to the user
- Relies on consistency with its interconnected elements
- One-off customizations may provide harmful user experiences
At this time, the following are considered customizable at the shared level only:
- Error states
- Warning states
- Disabled states
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
- Enclose the component within a custom scope (e.g. class or element)
- Use
::part(proposed) - [stub]
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:
background-colorandcolor
Kinetics
Styling Hooks related to kinetics is TBD while it is still in early development.
SDS Migration
- Do not apply hooks to variants that are specific to SLDS.
- Do not frame hooks around elements that are specific to SLDS.
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
- A hook is a valid candidate for consideration of addition if there’s been sufficient evidence of past customer behavior (overrides, requests, any clear signal).
- Even if the above is true, a hook should be validated through the request process and SDS working group before moving into implementation.
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
| Identifier | Example | Description | Required |
|---|---|---|---|
Namespace | sds | The namespace of the system that owns the hook | ✓ |
Scope | `c | s` | The scope defines the reach of your hook |
Context | button | The name of the component or feature that is targeted by the hook | ✓ |
Variant | brand | A variation of the component | |
Element | icon | The descendent element of the component | |
Category | color | The category of the property that the hook affects | ✓ |
Property | background | The semantic UI property being affected | ✓ |
Attribute | contrast | The semantic characteristic of a property | |
State | pressed | The state of a property within context of interaction design | |
Psuedo State | hover | The 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.
| Identifier | Example | Description | Required |
|---|---|---|---|
Namespace | sds | The namespace of the system that owns the hook | ✓ |
Scope | g | The scope defines the reach of your hook | ✓ |
Element | link | The descendent element of the component | |
Category | color | The category of the property that the hook affects | ✓ |
Property | background | The semantic UI property being affected | |
Attribute | neutral | The semantic characteristic of a property | |
Psuedo State | hover | The pseudo class that matches a user interaction | |
Range | 1 | The 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:
- Global Hooks: holistically changes the customer’s application.
- Shared Hooks: affects logical grouping of components.
- Component Hooks: affects only a single type of component.
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.
| Category | Property | Hook Convention | Usage Description |
|---|---|---|---|
| Color | Background | -color-background- | Generic UI background colors |
| Border | -color-border- | Generic UI border colors | |
| Link | -color-link- | Generic UI text link colors | |
| Font | Style | -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 | |
| Spacing | Inline | -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 | |
| Radius | Radius | -radius- | Generic UI radius values |
| Sizing | Border | -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 | |
| Shadow | Shadow | -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:
- Styling hook has multiple instances in the application or component that are expected to share the same value
- Styling hook does not describe an
-element-of the application or component
/* 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:
- Styling hook is specific to an element and not reusable across other properties
- Styling hook does not share a relationship with another part of the component
/* Example Explicit Hooks */
/* Explicit hook in accordion */
--sds-c-accordion-summary-color-background
/* Explicit hook in card */
--sds-c-card-footer-spacing-block-start