CSS / SCSS Guidelines

Architecture overview

Cynosure component styles follow a token-first, utility-first approach:

  1. Design tokens — exported from Figma as CSS custom properties; consumed everywhere
  2. Bootstrap 5 utilities — primary layout and spacing tool; write custom SCSS only for things Bootstrap cannot do
  3. Component SCSS — scoped with the c- BEM prefix; never leaks outside the component

The SCSS entry point is src/handoff/sass/main.scss. Handoff automatically injects this into every component's compiled CSS. Do not @import or @use it inside a component's style.scss.

Design tokens

Design tokens are exported from the Figma project (3GcQn3eA8Kg9kprYXBXksv) and compiled to CSS custom properties at build time. Token CSS files are available at:

Code
/api/component/tokens/css/colors.css /api/component/tokens/css/effects.css /api/component/tokens/css/typography.css /api/component/tokens/css/button.css /api/component/tokens/css/alert.css /api/component/tokens/css/badge.css /api/component/tokens/css/input.css /api/component/tokens/css/modal.css /api/component/tokens/css/select.css /api/component/tokens/css/tooltip.css

Key color tokens

CSS
1/* Brand */ 2--color-primary-orange: #ff4713 3--color-primary-orange-light: #ff6c42 4--color-primary-orange-dark: #cc390f 5--color-secondary-purple: #6f2eff 6--color-secondary-purple-light: #8c58ff 7--color-secondary-purple-dark: #5925cc 8--color-tertiary-pink: #db3381 9--color-tertiary-plumb: #361f50 10 11/* Neutrals */ 12--color-neutral-gray-100: #f4f4f4 13--color-neutral-gray-300: #d0d2d3 14--color-neutral-gray-600: #696969 15--color-neutral-gray-900: #231f20 16--color-neutral-white: #ffffff 17--color-neutral-black: #000000 18 19/* Text */ 20--color-text-hard: #000000 21--color-text-base: rgba(0, 0, 0, .8) 22--color-text-soft: #555555 23--color-text-muted: rgba(85, 85, 85, .8) 24 25/* Text on dark backgrounds */ 26--color-text-hard-invert: #ffffff 27--color-text-base-invert: rgba(255, 255, 255, .85) 28--color-text-muted-invert: rgba(255, 255, 255, .45) 29 30/* Borders */ 31--color-border-base: rgba(105, 105, 105, .15) 32--color-border-dark: rgba(105, 105, 105, .3) 33--color-border-invert: rgba(208, 210, 211, .2) 34 35/* Gradients */ 36--color-gradient-primary: linear-gradient(90deg, #ff4713 0%, #dc001b 100%) 37--color-gradient-energy-blends: linear-gradient(90deg, #db3381 0%, #361f50 100%) 38--color-gradient-hero-dark: linear-gradient(90deg, #434a50 0%, #231f20 100%) 39 40/* Status */ 41--color-extra-success: #49c781 42--color-extra-warning: #e6e448 43--color-extra-danger: #f25454 44--color-extra-info: #c0e1f0

Key typography tokens

CSS
1/* Font families */ 2--font-family-juana: 'Juana' 3--font-family-open-sans: 'Open Sans' 4 5/* Heading scale */ 6--typography-heading-1-font-size: 48px 7--typography-heading-1-font-family: 'Juana' 8--typography-heading-1-font-weight: 400 9--typography-heading-1-line-height: 1.4 10 11--typography-heading-2-font-size: 36px 12--typography-heading-2-font-family: 'Juana' 13--typography-heading-2-font-weight: 400 14 15--typography-heading-3-font-size: 28px 16--typography-heading-4-font-size: 24px 17--typography-heading-5-font-size: 20px

Key effect tokens

CSS
1--effect-shadow-base-300: 0px 1px 2px rgba(0, 0, 0, .15) 2--effect-shadow-base-500: 0px 2px 8px rgba(0, 0, 0, .1) 3--effect-shadow-base-700: 0px 4px 4px rgba(0, 0, 0, .25)

Always use CSS custom properties — never hardcode hex values or shadows in component SCSS.

SCSS
1// Good 2.c-card { 3 background: var(--color-neutral-white); 4 border-top: 3px solid var(--color-primary-orange); 5 box-shadow: var(--effect-shadow-base-500); 6 color: var(--color-text-base); 7} 8 9// Bad 10.c-card { 11 background: #ffffff; 12 border-top: 3px solid #ff4713; 13 box-shadow: 0px 2px 8px rgba(0, 0, 0, .1); 14 color: rgba(0, 0, 0, .8); 15}

BEM naming with the c- prefix

All Cynosure component classes use the c- prefix combined with BEM structure:

Code
.c-{component} Block .c-{component}__element Element .c-{component}--modifier Modifier

Examples from the codebase:

SCSS
1.c-accordion { ... } // Block 2.c-accordion__toggle { ... } // Element 3.c-accordion__panel { ... } // Element 4 5.c-header { ... } // Block 6.c-header__bottom { ... } // Element (sticky nav strip) 7.c-header__top { ... } // Element 8 9.c-select { ... } // Block 10.c-select__dropdown { ... } // Element (Select2 dropdown CSS class) 11.c-select--gray { ... } // Modifier 12 13.c-provider-finder { ... } // Block 14.c-provider-finder__select { ... } // Element 15.c-provider-finder__counter { ... } // Element

Keep class names scoped to the component — never write global element selectors (h2 { ... }) inside a component style.scss.

Path aliases

The Vite build exposes two path aliases for importing assets inside SCSS:

SCSS
1/* @public → src/handoff/public/ */ 2@use '@public/fonts/juana/Juana-Regular.woff2'; 3 4/* @export → src/handoff/exported/3GcQn3eA8Kg9kprYXBXksv/ */ 5@use '@export/tokens/css/colors.css';

These are defined in handoff.config.js:

JavaScript
1resolve.alias['@public'] = path.resolve(__dirname, 'public'); 2resolve.alias['@export'] = path.resolve(__dirname, 'exported', '3GcQn3eA8Kg9kprYXBXksv');

Component scoping

Never @import, @use, or @forward main.scss inside a component style.scss. Handoff automatically injects the shared stylesheet during compilation.

SCSS
1// Good — component-scoped styles only 2.c-hero-basic { 3 position: relative; 4 overflow: hidden; 5} 6 7.c-hero-basic__media { 8 aspect-ratio: 16 / 9; 9} 10 11// Bad — do not re-import the shared bundle 12@use '../../../sass/main.scss';13@import '../../../sass/main';

SCSS tooling

LibraryPurpose
sass-mqMedia query mixins with named breakpoints (sm: 576px, md: 769px, lg: 1254px)
modular-scaleTypographic scale functions
Normalize.cssCSS reset (included in main.scss)
Bootstrap SCSSUtility classes, grid, component base styles

Use the sass-mq breakpoints defined in handoff.config.js:

SCSS
1// Named breakpoints matching handoff.config.js 2// sm: 576px md: 769px lg: 1254px 3 4@include mq($from: md) { 5 .c-hero-basic__headline { 6 font-size: var(--typography-heading-1-font-size); 7 } 8} 9 10@include mq($until: sm) { 11 .c-hero-basic__cta-group { 12 flex-direction: column; 13 } 14}

SCSS syntax rules

  • Prefer @use and @forward over @import in new code (Sass modules)
  • Keep nesting to a maximum of 2 levels — do not nest BEM elements inside other BEM elements
  • Prefer Bootstrap utility classes for spacing, flex, and text alignment; write custom SCSS only when Bootstrap cannot express the design
  • Never use !important unless overriding a third-party vendor style (Slick, Select2, Magnific Popup)
  • Use :focus-visible for focus styles — not :focus alone
  • Add @media (prefers-reduced-motion: reduce) handling for any transition or animation
SCSS
1.c-hero-basic__image { 2 transition: transform 0.3s ease; 3 4 @media (prefers-reduced-motion: reduce) { 5 transition: none; 6 } 7}

Linting

  • Run Stylelint before committing
  • Run Prettier with the SCSS formatter for consistent formatting
  • Follow the project's Stylelint config for property ordering and selector conventions