name scss-style-guide description SCSS/CSS coding standards and patterns for fundamental-styles project
SCSS Coding Standards for fundamental-styles
When to use this skill:
Writing new SCSS component files
Refactoring existing SCSS code
Reviewing SCSS changes
Questions about fundamental-styles CSS architecture
1. Architecture & Methodology
BEM (Block Element Modifier) Pattern
Structure:
$block : #{$fd-namespace }-component-name;
.#{$block } {
&__element {
}
&--modifier {
}
}
Naming convention:
Block: .fd-{component} (e.g., .fd-card, .fd-button)
Element: .fd-{component}__{element} (e.g., .fd-card__header, .fd-button__text)
Modifier: .fd-{component}--{modifier} (e.g., .fd-card--interactive, .fd-button--emphasized)
Rules:
Use strict BEM - no deviations
Block variable format: $block: #{$fd-namespace}-component-name;
Use Sass nesting with & for elements and modifiers
The ampersand creates single-level class selectors, not nested ones
Self-Contained Components
Each component must be completely independent:
✅ DO:
.#{$block } {
&__content {
}
}
❌ DON'T:
.#{$block } {
.#{$fd-namespace }-button {
color : red;
}
}
If you need to adjust a child component:
Create a specific modifier class for that component
Example: Instead of .fd-card .fd-button { }, create .fd-button--in-card { }
Document the relationship in both component files
Why this matters:
Prevents global style bleeding
Eliminates package version conflicts
Components render correctly in isolation
No unexpected side effects
2. File Structure & Imports
Standard file header:
@use 'sass:map' ;
@import './new-settings' ;
@import './mixins' ;
$block : #{$fd-namespace }-component-name;
$other-component : #{$fd-namespace }-other-component;
$fd-component-sizes : (
's' : (
...
),
'm' : (
...
)
);
.#{$block } {
}
3. Reset Mixin Rules
The @include fd-reset(); mixin is MANDATORY:
.#{$block } {
@include fd-reset();
&__element {
@include fd-reset();
}
&__another-element {
@include fd-reset();
}
&--modifier {
--fdComponent_Property : new-value;
}
}
Why:
Resets browser default styles
Ensures consistent baseline
Prevents inheritance issues
Modifiers only override, so they don't need reset
4. CSS Custom Properties (Variables) Pattern
Use CSS variables for ALL state variations and customization:
.#{$block } {
--fdCard_Border : 0.0625rem solid var (--sapTile_BorderColor);
--fdCard_Background : var (--sapTile_Background);
--fdCard_Box_Shadow : var (--sapContent_Shadow0);
border : var (--fdCard_Border);
background : var (--fdCard_Background);
box-shadow : var (--fdCard_Box_Shadow);
&--interactive {
@include fd-hover() {
--fdCard_Background : var (--sapTile_Hover_Background);
--fdCard_Box_Shadow : var (--sapContent_Shadow1);
}
@include fd-active() {
--fdCard_Background : var (--sapTile_Active_Background);
}
}
&--transparent {
--fdCard_Background : transparent;
--fdCard_Border : none;
}
}
❌ WRONG - Don't set properties in modifiers:
.#{$block } {
background : var (--sapTile_Background);
&--transparent {
background : transparent;
}
}
✅ RIGHT - Override the variable:
.#{$block } {
--fdCard_Background : var (--sapTile_Background);
background : var (--fdCard_Background);
&--transparent {
--fdCard_Background : transparent;
}
}
Benefits:
Lower specificity (modifiers can be combined easily)
Easier theming and customization
States don't conflict with each other
Less CSS output
Enables runtime customization
Variable naming convention:
Format: --fd{Component}_{Property}_{Context}
Examples: --fdCard_Background, --fdButton_Border_Hover, --fdAvatar_Font_Size
Use PascalCase for component name, snake_case for rest
5. Loops for Repetitive Properties
Use @each loops for color sets, sizes, variations:
$fd-avatar-accent-colors : (
'1' : (
'border-color' : var (--sapAccentColor1),
'text-color' : var (--sapContent_ContrastTextColor),
'background-color' : var (--sapAccentColor1)
),
'2' : (
'border-color' : var (--sapAccentColor2),
'text-color' : var (--sapContent_ContrastTextColor),
'background-color' : var (--sapAccentColor2)
) // ... more colors
);
@each $set-name , $color-set in $fd-avatar-accent-colors {
.#{$block }--accent-color- #{$set-name } {
--fdAvatarBorderColor : #{map.get ($color-set, 'border-color')};
--fdAvatarColor : #{map.get ($color-set, 'text-color')};
--fdAvatarBackgroundColor : #{map.get ($color-set, 'background-color')};
}
}
$fd-avatar-sizes : (
'xs' : (
'ratio' : 2rem ,
'font-size' : 0.75rem ,
'offset' : 0.125rem
),
's' : (
'ratio' : 3rem ,
'font-size' : 1rem ,
'offset' :
) // ... more sizes
);
, in {
.#{ }--#{ } {
: #{map ($size-set, 'ratio')};
: #{map ($size-set, 'font-size')};
: #{map ($size-set, 'offset')};
}
}
When to use loops:
Color variations (accent colors, semantic colors, indication colors)
Size variations (xs, s, m, l, xl)
Numbered variations (shell categories 1-16, legend colors 1-20)
Any pattern that repeats with systematic changes
6. Nesting Depth & Selectors
Maximum 3 Levels of Nesting
✅ GOOD:
.#{$block } {
&__element {
&--modifier {
color : red;
}
}
}
❌ BAD:
.#{$block } {
&__header {
.#{$block }__title {
span {
&:hover {
}
}
}
}
}
Keep Files "Flat"
Prefer single-class selectors:
.fd-card
.fd-card__header
.fd-card__header--interactive
Avoid:
❌ Catch-all selectors: *
❌ Direct descendant: .fd-card > div (use element classes)
❌ Tag selectors: .fd-card div (use element classes)
❌ Multiple class specificity: .fd-card.is-active.is-selected
Exception - When nesting is OK:
.#{$block } {
&:hover {
}
&:focus {
}
&::before {
}
&__element {
&:first-child {
}
&:last-child {
}
&:only-child {
}
}
@include fd-hover() {
}
@include fd-focus() {
}
}
7. Units & Values
Use Theming Variables First
color : var (--sapTextColor);
background : var (--sapTile_Background);
border-color : var (--sapTile_BorderColor);
font-size : var (--sapFontSize);
font-family : var (--sapFontFamily);
When No Theming Variable Exists, Use rem
padding : 1rem ;
margin : 0.5rem ;
border-radius : 0.25rem ;
gap : 0.125rem ;
width : 20rem ;
border-width : 0.0625rem ;
line -height : 1.5 ;
flex : 1 ;
z-index : 10 ;
margin : 0 ;
padding : 0 ;
width : 100% ;
height : 50% ;
❌ NEVER use px directly:
padding : 16px ;
margin : 8px ;
border-radius : 4px ;
Calculation with rem
padding-inline : calc (0.625rem - var (--sapButton_BorderWidth));
height : calc (100% - 2rem );
width : calc (50% - 1rem );
8. RTL-Aware Spacing and Borders - CRITICAL
Always use logical properties or RTL-aware mixins for directional spacing and borders.
Use Logical Properties for Spacing
Logical properties automatically handle RTL direction:
✅ DO - Use logical properties:
.#{$block } {
margin-block : 1rem ;
margin-block-start : 1rem ;
margin-block-end : 1rem ;
padding-block : 1rem ;
padding-block-start : 1rem ;
padding-block-end : 1rem ;
margin-inline : 1rem ;
margin-inline-start : 1rem ;
margin-inline-end : 1rem ;
padding-inline : 1rem ;
padding-inline-start : 1rem ;
padding-inline-end : 1rem ;
inset-inline-start : 0 ;
inset-inline-end : 0 ;
: ;
: ;
}
❌ DON'T - Avoid directional properties:
.#{$block } {
margin-left : 1rem ;
margin-right : 1rem ;
padding-left : 1rem ;
padding-right : 1rem ;
left : 0 ;
right : 0 ;
}
Use RTL-Aware Mixins When Needed
For cases where logical properties aren't sufficient, use provided mixins:
.#{$block } {
@include fd-set-position-right(1rem );
@include fd-set-position-left(1rem );
@include fd-set-margin-right(1rem );
@include fd-set-margin-left(1rem );
@include fd-set-padding-right(1rem );
@include fd-set-padding-left(1rem );
@include fd-rtl() {
direction : rtl;
text -align: right;
}
}
Borders - Use Logical Properties
Border logical properties also handle RTL automatically:
✅ DO - Use logical border properties:
.#{$block } {
border : 0.0625rem solid var (--sapTile_BorderColor);
border-inline : 0.0625rem solid var (--sapTile_BorderColor);
border-inline-start : 0.0625rem solid var (--sapTile_BorderColor);
border-inline-end : 0.0625rem solid var (--sapTile_BorderColor);
border-block : 0.0625rem solid var (--sapTile_BorderColor);
border-block-start : 0.0625rem solid var (--sapTile_BorderColor);
border-block-end : 0.0625rem solid var (--sapTile_BorderColor);
border-start-start-radius : 0.5rem ;
border-start-end-radius : 0.5rem ;
border-end-start-radius : 0.5rem ;
border-end-end-radius : 0.5rem ;
}
❌ DON'T - Avoid directional border properties:
.#{$block } {
border-left : 0.0625rem solid var (--sapTile_BorderColor);
border-right : 0.0625rem solid var (--sapTile_BorderColor);
border-top-left-radius : 0.5rem ;
border-top-right-radius : 0.5rem ;
}
Complete Example
.#{$block } {
@include fd-reset();
padding-block-start : 1rem ;
padding-block-end : 1rem ;
margin-block : 0.5rem ;
padding-inline-start : 1rem ;
padding-inline-end : 0.5rem ;
margin-inline-start : 0.25rem ;
border-inline-start : 0.0625rem solid var (--sapTile_BorderColor);
inset-inline-start : 0 ;
inset-block-start : 0 ;
&__icon {
@include fd-reset();
@include fd-set-position-right(0.5rem );
@include fd-set-margin-left(0.25rem );
}
@include fd-rtl() {
direction : rtl;
}
}
Why this matters:
Fundamental-styles supports both LTR (left-to-right) and RTL (right-to-left) languages
Logical properties automatically adapt to text direction
Using directional properties breaks RTL layouts
No need to write separate RTL overrides for spacing and borders
9. State & Pseudo-Class Mixins
Use provided mixins for interactive states:
.#{$block } {
&--interactive {
cursor : pointer;
@include fd-hover() {
--fdCard_Background : var (--sapTile_Hover_Background);
--fdCard_Box_Shadow : var (--sapContent_Shadow1);
}
@include fd-active() {
--fdCard_Background : var (--sapTile_Active_Background);
--fdCard_Box_Shadow : none;
}
@include fd-focus() {
outline : var (--sapContent_FocusWidth) var (--sapContent_FocusStyle) var (--sapContent_FocusColor);
outline-offset : var (--sapContent_FocusOffset);
}
@include fd-disabled() {
opacity : var (--sapContent_DisabledOpacity);
cursor : not-allowed;
}
@include fd-selected() {
--fdCard_Border : var (--sapContent_FocusWidth) solid var (--sapContent_FocusColor);
}
@include fd-toggled() {
--fdButton_Background : var (--sapButton_Selected_Background);
}
}
}
Available state mixins:
@include fd-hover() - Hover state
@include fd-active() - Active/pressed state
@include fd-focus() - Keyboard focus state
@include fd-disabled() - Disabled state
@include fd-selected() - Selected state (for lists, tables)
@include fd-toggled() - Toggled state (for buttons, switches)
@include fd-readonly() - Read-only state
10. Responsive & Compact Modes
Responsive Breakpoints
.#{$block } {
--fdCard_Padding : 3rem ;
@include fd-media-md() {
--fdCard_Padding : 2rem ;
}
@include fd-media-sm() {
--fdCard_Padding : 1rem ;
--fdCard_Flex_Direction : column;
}
}
Compact/Condensed Mode
.#{$block } {
--fdButton_Height : var (--sapElement_Height);
--fdButton_Padding : 0.625rem ;
@include fd-compact-or-condensed() {
--fdButton_Height : var (--sapElement_Compact_Height);
--fdButton_Padding : 0.5rem ;
}
}
11. Common Mixins & Utilities
Flexbox Layouts
@include fd-flex();
@include fd-flex(column);
@include fd-flex() {
gap : 1rem ;
}
@include fd-flex-center();
@include fd-flex-vertical-center();
@include fd-inline-flex-center();
@include fd-flex(column) {
gap : 0.5rem ;
align-items : flex-start;
}
Positioning (RTL-aware)
@include fd-set-position-right(1rem );
@include fd-set-position-left(1rem );
@include fd-set-margin-right(1rem );
@include fd-set-margin-left(1rem );
@include fd-set-padding-right(1rem );
@include fd-set-padding-left(1rem );
@include fd-rtl() {
direction : rtl;
}
Text Utilities
@include fd-ellipsis();
Borders
@include fd-set-border(var(--sapBorderColor));
Sizing
@include fd-square(2rem );
Reset Spacing
@include fd-reset-spacing();
12. Pseudo-Elements for Visual Effects
Extended Touch Area
.#{$block }--interactive {
position : relative;
&::before {
content : '' ;
display : block;
position : absolute;
inset : -0.5rem ;
}
}
Custom Focus Indicator
.#{$block } {
position : relative;
@include fd-focus() {
outline : none;
&::after {
content : '' ;
position : absolute;
display : block;
border : var (--sapContent_FocusWidth) var (--sapContent_FocusStyle) var (--sapContent_FocusColor);
inset : 0.125rem ;
border-radius : var (--fdCard_Focus_Outline_Radius);
z-index : 3 ;
}
}
}
Badge Positioning
.#{$block } {
position : relative;
&__badge {
@include fd-reset();
@include fd-set-position-right(var(--fdButton_Badge_Offset));
position : absolute;
top : var (--fdButton_Badge_Offset);
z-index : 1 ;
}
}
13. Code Organization Within File
Follow this order for consistency:
$block : #{$fd-namespace }-component-name;
$fd-component-sizes : (...);
$fd-component-colors : (...);
@mixin fd-component-helper() {
}
.#{$block } {
--fdComponent_Property : value;
--fdComponent_Other_Property : value;
@include fd-reset();
@include fd-flex();
position : relative;
display : flex;
padding : 1rem ;
margin : 0 ;
font-size : var (--sapFontSize);
color : var (--sapTextColor);
background : var (--fdComponent_Background);
border : var (--fdComponent_Border);
box-shadow : var (--fdComponent_Box_Shadow);
&::before {
content : '' ;
}
&__element-name {
@include fd-reset();
}
&__another-element {
@include fd-reset();
}
& {
: new-value;
}
& {
: another-value;
}
& {
: pointer;
fd-hover() {
: (--sapTile_Hover_Background);
}
fd-active() {
: (--sapTile_Active_Background);
}
}
, in {
&--#{ } {
: #{map ($size-set, 'width')};
}
}
fd-compact-or-condensed() {
: ;
}
fd-media-sm() {
: column;
}
}
14. Anti-Patterns to AVOID
❌ Don't Override Other Components
.#{$block } {
.#{$fd-namespace }-button {
color : red;
}
.#{$fd-namespace }-list__item {
padding : 0 ;
}
}
❌ Don't Set Properties in Modifiers When Variables Exist
.#{$block } {
background : var (--fdCard_Background);
&--hover {
background : var (--sapTile_Hover_Background);
}
}
.#{$block } {
--fdCard_Background : var (--sapTile_Background);
background : var (--fdCard_Background);
&--hover {
--fdCard_Background : var (--sapTile_Hover_Background);
}
}
❌ Don't Use Deep Nesting
.#{$block } {
&__header {
.#{$block }__title {
span {
&:hover {
}
}
}
}
}
.#{$block } {
&__header {
}
&__title {
&:hover {
}
}
}
❌ Don't Use Tag or Universal Selectors
.#{$block } {
div {
padding : 1rem ;
}
* {
box-sizing : border-box;
}
}
.#{$block } {
&__content {
padding : 1rem ;
}
}
❌ Don't Forget fd-reset()
.#{$block }__element {
color : red;
}
.#{$block }__element {
@include fd-reset();
color : red;
}
❌ Don't Use px Units
.#{$block } {
padding : 16px ;
margin : 8px ;
border-radius : 4px ;
}
.#{$block } {
padding : 1rem ;
margin : 0.5rem ;
border-radius : 0.25rem ;
}
❌ Don't Create Duplicate Loops
.#{$block }--accent-color-1 {
--fdAvatar_Color : var (--sapAccentColor1);
}
.#{$block }--accent-color-2 {
--fdAvatar_Color : var (--sapAccentColor2);
}
.#{$block }--accent-color-3 {
--fdAvatar_Color : var (--sapAccentColor3);
}
@each $num in (1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) {
.#{$block }--accent-color- #{$num } {
--fdAvatar_Color : var (--sapAccentColor#{$num });
}
}
❌ Don't Use Directional Properties (Breaks RTL)
.#{$block } {
margin-left : 1rem ;
margin-right : 0.5rem ;
padding-left : 1rem ;
border-left : 0.0625rem solid var (--sapTile_BorderColor);
left : 0 ;
right : auto;
}
.#{$block } {
margin-inline-start : 1rem ;
margin-inline-end : 0.5rem ;
padding-inline-start : 1rem ;
border-inline-start : 0.0625rem solid var (--sapTile_BorderColor);
inset-inline-start : 0 ;
inset-inline-end : auto;
}
.#{$block } {
@include fd-set-margin-left(1rem );
@include fd-set-position-right(0 );
}
❌ Don't Forget Logical Border Radius
.#{$block } {
border-top-left-radius : 0.5rem ;
border-top-right-radius : 0.5rem ;
border-bottom-left-radius : 0 ;
border-bottom-right-radius : 0 ;
}
.#{$block } {
border-start-start-radius : 0.5rem ;
border-start-end-radius : 0.5rem ;
border-end-start-radius : 0 ;
border-end-end-radius : 0 ;
}
.#{$block } {
border-radius : 0.5rem 0.5rem 0 0 ;
}
15. Accessibility Patterns
.#{$block } {
&[aria-disabled='true' ] ,
&[disabled] {
@include fd-disabled() {
opacity : var (--sapContent_DisabledOpacity);
cursor : not-allowed;
pointer-events : none;
}
}
&[aria-selected='true' ] {
@include fd-selected() {
--fdComponent_Background : var (--sapList_SelectionBackgroundColor);
--fdComponent_Border_Color : var (--sapList_SelectionBorderColor);
}
}
&[aria-expanded='true' ] {
--fdComponent_Icon_Rotation : 180deg ;
}
&[aria-pressed='true' ] {
@include fd-toggled() {
--fdButton_Background : var (--sapButton_Selected_Background);
}
}
&[aria-hidden='true' ] {
display : none;
}
}
16. Quick Checklist for Code Review
When writing or reviewing SCSS code, verify:
Structure & Naming:
Reset & Mixins:
CSS Variables:
Patterns & Best Practices:
Units & Values:
RTL Support:
Logical properties used for spacing: margin-inline-start, padding-block, etc. (not margin-left, padding-top)
Logical properties used for borders: border-inline-start, border-block-end, etc. (not border-left, border-top)
RTL-aware mixins used when logical properties aren't sufficient (fd-set-position-right, etc.)
No directional properties: left, right, margin-left, padding-right, border-left
Border radius uses logical properties when directional
Responsive & Breakpoints:
Accessibility:
17. Common Theming Variables Reference
Colors
Text:
--sapTextColor - Primary text
--sapContent_LabelColor - Labels
--sapLink_Color - Links
--sapContent_IconColor - Icons
Backgrounds:
--sapBackgroundColor - Page background
--sapTile_Background - Card/tile background
--sapGroup_ContentBackground - Content area background
--sapButton_Background - Button background
Borders:
--sapTile_BorderColor - Tile borders
--sapGroup_ContentBorderColor - Content borders
--sapButton_BorderColor - Button borders
Semantic Colors:
--sapPositiveColor - Success/green
--sapNegativeColor - Error/red
--sapCriticalColor - Warning/orange
--sapInformativeColor - Info/blue
--sapNeutralColor - Neutral/gray
Interactive States:
--sapTile_Hover_Background - Hover background
--sapTile_Active_Background - Active/pressed background
--sapButton_Selected_Background - Selected background
--sapContent_FocusColor - Focus outline color
Typography
--sapFontFamily - Default font family
--sapFontHeaderFamily - Header font family
--sapFontSize - Base font size (0.875rem)
--sapFontSmallSize - Small text
--sapFontLargeSize - Large text
--sapFontHeader1Size through --sapFontHeader6Size - Heading sizes
Spacing
--sapElement_Height - Standard element height (2.75rem)
--sapElement_Compact_Height - Compact element height (2rem)
--sapContent_FocusWidth - Focus outline width
--sapContent_FocusOffset - Focus outline offset
Effects
--sapContent_Shadow0 through --sapContent_Shadow3 - Shadow levels
--sapTile_BorderCornerRadius - Border radius for tiles
--sapButton_BorderCornerRadius - Border radius for buttons
--sapContent_DisabledOpacity - Opacity for disabled elements
Examples from Real Components
Example: Simple Component (Link)
$block : #{$fd-namespace }-link;
.#{$block } {
@include fd-reset();
color : var (--sapLink_Color);
text -decoration: var (--sapLink_TextDecoration);
cursor : pointer;
@include fd-hover() {
color : var (--sapLink_Hover_Color);
text -decoration: var (--sapLink_Hover_TextDecoration);
}
@include fd-active() {
color : var (--sapLink_Active_Color);
}
@include fd-focus() {
outline : var (--sapContent_FocusWidth) var (--sapContent_FocusStyle) var (--sapContent_FocusColor);
outline-offset : 0.0625rem ;
}
&--subtle {
color : var (--sapLink_SubtleColor);
}
&--emphasized {
font-weight : bold;
}
}
Example: Complex Component (Card Header)
$block : #{$fd-namespace }-card;
.#{$block } {
--fdCard_Border : 0.0625rem solid var (--sapTile_BorderColor);
--fdCard_Background : var (--sapTile_Background);
--fdCard_Box_Shadow : var (--sapContent_Shadow0);
@include fd-reset();
@include fd-flex(column);
position : relative;
border : var (--fdCard_Border);
background : var (--fdCard_Background);
box-shadow : var (--fdCard_Box_Shadow);
border-radius : var (--sapTile_BorderCornerRadius);
&__header {
@include fd-reset();
padding : 1rem ;
border-bottom : 0.0625rem solid var (--sapTile_SeparatorColor);
}
&__header-main {
@include fd-reset();
@include fd-flex() {
gap : 0.5rem ;
align-items : center;
}
}
&__title {
@include fd-reset();
font-family : var (--sapFontHeaderFamily);
font-size : var (--sapFontHeader3Size);
color : var (--sapTile_TitleTextColor);
}
&--interactive {
cursor : pointer;
@include fd-hover() {
--fdCard_Background : (--sapTile_Hover_Background);
: (--sapContent_Shadow1);
}
fd-active() {
: (--sapTile_Active_Background);
: none;
}
fd-focus() {
: (--sapContent_FocusWidth) solid (--sapContent_FocusColor);
: ;
}
}
fd-compact-or-condensed() {
&__header {
: ;
}
}
}
Remember: These standards exist to ensure consistency, maintainability, and accessibility across the entire fundamental-styles library. When in doubt, look at similar existing components for reference patterns.