The Integration Strategy¶
1. Your Current Setup Maps Well¶
Your ITCSS-like structure already aligns with design token architecture:
- 02-tokens (your current layer) → Option Tokens (color palettes, spacing scales)
- 03-layout → Uses Decision Tokens (semantic decisions like "surface color", "primary spacing")
- 04-utils → Helper classes that might use Decision or Component Tokens
- 05-components → Component Tokens (button-primary-background, card-padding, etc.)
2. Quasar Integration Strategy¶
Quasar provides extensive SCSS variables (as you've shown). Here's how to approach it:
Option A: Override Quasar Variables with Your Tokens
// 02-tokens/_quasar-overrides.scss
@use "design-tokens" as tokens;
// Map your design tokens to Quasar variables
$primary: tokens.$color-brand-primary;
$secondary: tokens.$color-brand-secondary;
$space-base: tokens.$spacing-base;
$breakpoint-xs: tokens.$breakpoint-mobile;
// Import Quasar with your overrides
@import "quasar/src/css/variables.sass";
Option B: Create a Token Mapping Layer
// 02-tokens/_design-tokens.scss
// Your source of truth
$color-brand-primary: #1976d2;
$color-brand-secondary: #26a69a;
$spacing-base: 16px;
// 02-tokens/_quasar-bridge.scss
// Bridge layer that maps tokens to Quasar
@use "design-tokens" as tokens;
$primary: tokens.$color-brand-primary;
$secondary: tokens.$color-brand-secondary;
$space-base: tokens.$spacing-base;
3. Atomic Design + BEM Integration¶
Since your designer uses Atomic Design, organize your components layer:
05-components/
├── _atoms/ # Basic elements (buttons, inputs)
│ ├── _button.scss
│ └── _input.scss
├── _molecules/ # Simple combinations (search bar, card header)
│ ├── _search-bar.scss
│ └── _card-header.scss
├── _organisms/ # Complex sections (navigation, footer)
│ ├── _navigation.scss
│ └── _footer.scss
└── _templates/ # Page-level layouts
└── _dashboard.scss
BEM with Component Tokens:
// 05-components/_atoms/_button.scss
@use "../../02-tokens/decisions" as decisions;
.button {
// Base button using decision tokens
padding: decisions.$button-padding;
border-radius: decisions.$button-border-radius;
font-size: decisions.$button-font-size;
&--primary {
// Component token: button-primary
background: decisions.$button-primary-background;
color: decisions.$button-primary-text;
}
&--secondary {
background: decisions.$button-secondary-background;
color: decisions.$button-secondary-text;
}
&__icon {
// Element within button
margin-right: decisions.$button-icon-spacing;
}
}
4. Three-Layer Token Architecture¶
Layer 1: Options (Primitives)
// 02-tokens/_options.scss
// What's available (the palette)
$color-blue-900: #0265dc;
$color-grey-100: #f8f8f8;
$spacing-4: 4px;
$spacing-8: 8px;
$spacing-16: 16px;
Layer 2: Decisions (Semantic)
// 02-tokens/_decisions.scss
@use "options" as opt;
// How they're applied
$color-primary: opt.$color-blue-900;
$color-surface: opt.$color-grey-100;
$spacing-small: opt.$spacing-8;
$spacing-medium: opt.$spacing-16;
$button-padding: $spacing-small $spacing-medium;
$button-border-radius: 4px;
Layer 3: Component Tokens
// 02-tokens/_components.scss
@use "decisions" as dec;
// Where they're applied
$button-primary-background: dec.$color-primary;
$button-primary-text: dec.$color-text-on-primary;
$button-secondary-background: dec.$color-surface;
$button-secondary-text: dec.$color-text;
5. File Structure Example¶
styles/
├── 01-reset/
│ └── _normalize.scss
├── 02-tokens/
│ ├── _options.scss # Primitives (private)
│ ├── _decisions.scss # Semantic tokens
│ ├── _components.scss # Component-specific
│ ├── _quasar-bridge.scss # Maps to Quasar vars
│ └── _index.scss # Public API
├── 03-layout/
│ ├── _grid.scss
│ └── _container.scss
├── 04-utils/
│ ├── _spacing.scss
│ └── _text.scss
├── 05-components/
│ ├── _atoms/
│ ├── _molecules/
│ └── _organisms/
└── main.scss
6. CSS Layers Implementation¶
// main.scss
@layer reset, tokens, layout, utils, components;
@layer reset {
@import "01-reset/normalize";
}
@layer tokens {
// Tokens generate CSS custom properties
@import "02-tokens/index";
}
@layer layout {
@import "03-layout/grid";
@import "03-layout/container";
}
@layer utils {
@import "04-utils/spacing";
@import "04-utils/text";
}
@layer components {
@import "05-components/atoms/button";
@import "05-components/molecules/search-bar";
}
7. Practical Workflow¶
- Designer creates in Figma (Atomic Design structure)
- Extract design tokens → Goes into
02-tokens/_options.scssand_decisions.scss - Map to Quasar → Override Quasar variables via
_quasar-bridge.scss - Build components → Use BEM methodology with token references
- Non-Quasar components → Build using your tokens in the components layer
8. Example: Building a Custom Button¶
// 02-tokens/_decisions.scss
$button-padding-y: 8px;
$button-padding-x: 16px;
$button-radius: 4px;
// 05-components/_atoms/_button.scss
.btn {
// Use decision tokens
padding: $button-padding-y $button-padding-x;
border-radius: $button-radius;
&--primary {
background: $color-primary;
color: $color-text-on-primary;
}
&--outline {
border: 2px solid $color-primary;
color: $color-primary;
}
&__icon {
margin-right: $spacing-small;
}
}
Key Recommendations¶
- Start with 2 layers (Options + Decisions) - add Component tokens later if needed
- Make Option tokens private - only expose Decision/Component tokens to developers
- Keep Quasar as-is for standard components, override where needed
- Use your token layer as the bridge between design and Quasar
- BEM for custom components - leverage tokens for values
- Document which tokens are public in your
02-tokens/_index.scss
This approach gives you:
- ✅ Design token architecture (flexibility + automation potential)
- ✅ ITCSS organization (scalability)
- ✅ Atomic Design (designer alignment)
- ✅ BEM (component clarity)
- ✅ Quasar integration (framework leverage)
Complete break down¶
// ============================================================================
// 02-TOKENS LAYER - DESIGN TOKEN ARCHITECTURE
// ============================================================================
//
// WHAT ARE THESE THREE LAYERS?
// -----------------------------
// This is NOT SCSS-specific - it's a design token architecture pattern
// from the "Design Token-Based UI Architecture" methodology.
//
// Think of it as THREE LEVELS OF DECISION-MAKING:
//
// ┌─────────────────────────────────────────────────────────────────┐
// │ LEVEL 1: OPTIONS (What CAN we use?) │
// │ ├─ Question: "What colors/sizes are available in our palette?" │
// │ └─ Example: blue-500, blue-600, spacing-4, spacing-8 │
// └─────────────────────────────────────────────────────────────────┘
// ↓ (select from options)
// ┌─────────────────────────────────────────────────────────────────┐
// │ LEVEL 2: DECISIONS (How SHOULD we use them?) │
// │ ├─ Question: "What does primary color mean? What's our base │
// │ │ spacing unit?" │
// │ └─ Example: primary = blue-600, spacing-md = spacing-4 │
// └─────────────────────────────────────────────────────────────────┘
// ↓ (apply decisions to components)
// ┌─────────────────────────────────────────────────────────────────┐
// │ LEVEL 3: COMPONENTS (Where DO we apply them?) │
// │ ├─ Question: "What's the button's color? Card's padding?" │
// │ └─ Example: button-bg = primary, card-padding = spacing-md │
// └─────────────────────────────────────────────────────────────────┘
//
// WHY THREE LAYERS?
// -----------------
// 1. FLEXIBILITY: Change "primary" from blue to green without touching
// every component
// 2. CONSISTENCY: All "primary" colors update together
// 3. SCALABILITY: Add new components easily by referencing decisions
// 4. MAINTAINABILITY: Changes flow naturally down the hierarchy
//
// REAL-WORLD ANALOGY:
// -------------------
// OPTIONS = Paint store (all available paint colors)
// DECISIONS = Interior designer's palette (colors chosen for the house)
// COMPONENTS = Room assignments (living room = warm-beige, bedroom = cool-blue)
//
// If you change your mind about "warm-beige", you only update the DECISION,
// not every room individually.
//
// HOW DOES THIS WORK IN SCSS?
// ----------------------------
// We use SCSS modules (@use, @forward) to create this hierarchy:
// - _options.scss defines primitives
// - _decisions.scss imports options and creates semantic names
// - _components.scss imports decisions and creates component-specific tokens
// - _index.scss forwards only decisions & components (keeps options private)
//
// RESULT: Developers only see "primary" and "button-bg", not "blue-600"
//
// ----------------------------------------------------------------------------
// 02-tokens/_options.scss (PRIVATE - Primitives/Foundation)
// ----------------------------------------------------------------------------
// LAYER 1: OPTIONS (What's Available)
//
// PURPOSE: Define the raw palette - all possible values we COULD use
// ANALOGY: This is like the paint store's full color chart
// WHO USES: Only other token files (decisions.scss), NOT developers directly
// VISIBILITY: PRIVATE - not exposed to developers (see _index.scss)
//
// WHY KEEP THIS PRIVATE?
// 1. Prevents developers from using random values like $spacing-3 everywhere
// 2. Smaller bundle size (unused options don't bloat CSS)
// 3. Can change without breaking things (no one depends on it directly)
// 4. Forces use of semantic tokens (decisions/components) instead
//
// EXAMPLE SCENARIO:
// Bad: .my-component { padding: $spacing-3 } ← What does "3" mean?
// Good: .my-component { padding: $spacing-sm } ← Clear semantic meaning
// Spacing scale (based on 4px grid)
$spacing-1: 4px !default;
$spacing-2: 8px !default;
$spacing-3: 12px !default;
$spacing-4: 16px !default;
$spacing-6: 24px !default;
$spacing-8: 32px !default;
// Border radius options
$radius-sm: 2px !default;
$radius-md: 4px !default;
$radius-lg: 8px !default;
$radius-xl: 16px !default;
$radius-full: 9999px !default;
// Font sizes
$font-size-xs: 12px !default;
$font-size-sm: 14px !default;
$font-size-md: 16px !default;
$font-size-lg: 18px !default;
// Font weights
$font-weight-normal: 400 !default;
$font-weight-medium: 500 !default;
$font-weight-bold: 700 !default;
// Line heights
$line-height-tight: 1.25 !default;
$line-height-normal: 1.5 !default;
$line-height-relaxed: 1.715 !default;
// Color primitives
$color-blue-500: #2196f3 !default;
$color-blue-600: #1976d2 !default;
$color-blue-700: #1565c0 !default;
$color-grey-100: #f5f5f5 !default;
$color-grey-200: #eeeeee !default;
$color-grey-700: #616161 !default;
$color-white: #ffffff !default;
// Shadow primitives (from Quasar's system)
$shadow-1:
0 1px 3px rgba(0, 0, 0, 0.2),
0 1px 1px rgba(0, 0, 0, 0.14),
0 2px 1px -1px rgba(0, 0, 0, 0.12) !default;
$shadow-2:
0 1px 5px rgba(0, 0, 0, 0.2),
0 2px 2px rgba(0, 0, 0, 0.14),
0 3px 1px -2px rgba(0, 0, 0, 0.12) !default;
$shadow-5:
0 3px 5px -1px rgba(0, 0, 0, 0.2),
0 5px 8px rgba(0, 0, 0, 0.14),
0 1px 14px rgba(0, 0, 0, 0.12) !default;
// Transition options
$transition-fast: 0.15s !default;
$transition-normal: 0.3s !default;
$transition-easing: cubic-bezier(0.25, 0.8, 0.5, 1) !default;
// ----------------------------------------------------------------------------
// 02-tokens/_decisions.scss (PUBLIC - Semantic/Contextual)
// ----------------------------------------------------------------------------
// LAYER 2: DECISIONS (How to Use Options)
//
// PURPOSE: Give semantic meaning to raw values - answer "what does it mean?"
// ANALOGY: Interior designer's color palette for the house
// WHO USES: Component files AND developers (this is PUBLIC)
// VISIBILITY: PUBLIC - exposed via _index.scss
//
// KEY CONCEPT: "SEMANTIC NAMING"
// Instead of: $color-blue-600 ← What is this used for?
// We have: $color-primary ← Aha! It's our brand color
//
// WHY THIS LAYER EXISTS?
// 1. Context: "primary" has meaning, "blue-600" is just a value
// 2. Changeability: Rebrand from blue to green? Change ONE line here
// 3. Consistency: All "primary" uses update together automatically
// 4. Developer Experience: No guessing which shade to use
//
// REAL EXAMPLE OF VALUE:
// Without decisions: Designer says "change brand color"
// → You search codebase for all "blue-600" (200+ files)
// → Miss some, inconsistent results
// With decisions:
// → Change $color-primary: blue-600 to $color-primary: green-600
// → Done! Everything updates
//
// REFERENCE PATTERN:
// Notice how we reference options using: opt.$option-name
// This is SCSS modules syntax - it imports from _options.scss
@use "options" as opt;
// Brand colors (semantic naming)
$color-primary: opt.$color-blue-600 !default;
$color-primary-dark: opt.$color-blue-700 !default;
$color-secondary: #26a69a !default;
// Surface colors
$color-surface: opt.$color-white !default;
$color-surface-variant: opt.$color-grey-100 !default;
$color-on-surface: opt.$color-grey-700 !default;
$color-on-primary: opt.$color-white !default;
// Spacing decisions (semantic naming)
$spacing-xs: opt.$spacing-1 !default;
$spacing-sm: opt.$spacing-2 !default;
$spacing-md: opt.$spacing-4 !default;
$spacing-lg: opt.$spacing-6 !default;
$spacing-xl: opt.$spacing-8 !default;
// Interactive element decisions
$interactive-radius: opt.$radius-md !default;
$interactive-radius-round: opt.$radius-full !default;
$interactive-shadow: opt.$shadow-2 !default;
$interactive-shadow-active: opt.$shadow-5 !default;
$interactive-transition: opt.$transition-normal opt.$transition-easing !default;
// Typography decisions
$text-body-size: opt.$font-size-sm !default;
$text-body-line-height: opt.$line-height-relaxed !default;
$text-weight-emphasis: opt.$font-weight-medium !default;
// ----------------------------------------------------------------------------
// 02-tokens/_components.scss (PUBLIC - Component-specific)
// ----------------------------------------------------------------------------
// LAYER 3: COMPONENTS (Where to Apply Decisions)
//
// PURPOSE: Map decisions to specific components - answer "where does it go?"
// ANALOGY: Room-by-room color assignments (living room = warm-beige)
// WHO USES: Component SCSS files (05-components/)
// VISIBILITY: PUBLIC - exposed via _index.scss
//
// KEY CONCEPT: "COMPONENT-SPECIFIC CONTEXT"
// Instead of: .button { background: $color-primary }
// We have: .button { background: $button-primary-bg }
// where $button-primary-bg = $color-primary
//
// WHY THIS LAYER EXISTS?
// 1. Clarity: "$button-primary-bg" is more specific than "$color-primary"
// 2. Flexibility: Buttons might need different shades in the future
// 3. Documentation: Self-documenting what each token is for
// 4. Component Isolation: Change button styles without affecting cards
//
// WHEN TO USE COMPONENT TOKENS VS DECISIONS?
//
// Use DECISIONS directly when:
// → Simple, one-off usage
// → No component-specific variations needed
// Example: .error-message { color: $color-error }
//
// Use COMPONENT TOKENS when:
// → Multiple related properties for same component
// → Might need component-specific variations later
// → Building a reusable component library
// Example: All button-related tokens grouped together
//
// REAL EXAMPLE SCENARIO:
// Imagine: "Make buttons slightly darker than the primary color"
// Without component tokens:
// → Change $color-primary → breaks cards, badges, etc.
// With component tokens:
// → Change $button-primary-bg = $color-primary-dark
// → Only buttons affected!
//
@use "decisions" as dec;
// Button component tokens
// Notice: We're creating a "namespace" for all button-related tokens
$button-padding-y: dec.$spacing-sm !default;
$button-padding-x: dec.$spacing-md !default;
$button-padding: $button-padding-y $button-padding-x !default;
$button-padding-dense: 0.285em !default;
$button-radius: dec.$interactive-radius !default;
$button-radius-rounded: dec.$interactive-radius-round !default;
$button-radius-push: 7px !default; // Kept from Quasar for compatibility
$button-font-size: dec.$text-body-size !default;
$button-line-height: dec.$text-body-line-height !default;
$button-font-weight: dec.$text-weight-emphasis !default;
$button-transition: dec.$interactive-transition !default;
$button-shadow: dec.$interactive-shadow !default;
$button-shadow-active: dec.$interactive-shadow-active !default;
// Button variant tokens (primary)
$button-primary-bg: dec.$color-primary !default;
$button-primary-text: dec.$color-on-primary !default;
$button-primary-bg-hover: dec.$color-primary-dark !default;
// Button variant tokens (secondary)
$button-secondary-bg: dec.$color-secondary !default;
$button-secondary-text: dec.$color-on-primary !default;
// Button variant tokens (outline)
$button-outline-border: dec.$color-primary !default;
$button-outline-text: dec.$color-primary !default;
$button-outline-bg-hover: dec.$color-surface-variant !default;
// FAB specific
$button-fab-icon-size: 24px !default;
// Card component tokens
$card-padding: dec.$spacing-lg !default;
$card-radius: dec.$interactive-radius !default;
$card-bg: dec.$color-surface !default;
$card-shadow: dec.$interactive-shadow !default;
$card-header-padding: dec.$spacing-md dec.$spacing-lg !default;
$card-body-padding: dec.$spacing-lg !default;
$card-footer-padding: dec.$spacing-md dec.$spacing-lg !default;
// ----------------------------------------------------------------------------
// 02-tokens/_quasar-bridge.scss
// ----------------------------------------------------------------------------
// THE BRIDGE: Connects Your Token System to Quasar Framework
//
// PURPOSE: Map your design tokens to Quasar's variable names
// WHY NEEDED: Quasar expects specific variable names like $button-padding
//
// HOW IT WORKS:
// 1. You define tokens: $button-padding: 8px 16px
// 2. Quasar expects: $button-padding variable
// 3. Bridge maps: $button-padding (Quasar) = $button-padding (yours)
//
// IMPORT ORDER IS CRITICAL:
// ┌─────────────────────────────────────────┐
// │ 1. Import this bridge file │ ← Sets variables
// │ 2. Import Quasar │ ← Uses those variables
// │ 3. Import your custom components │ ← Uses both
// └─────────────────────────────────────────┘
//
// If you import Quasar BEFORE the bridge:
// → Quasar uses its defaults (your tokens ignored!)
// → Your customizations don't apply
//
// WHAT !default MEANS:
// $variable: value !default;
// → "Use this value IF the variable isn't already defined"
// → Allows easy overriding later
// → Quasar uses !default everywhere for customizability
//
// TWO APPROACHES:
//
// Approach A - Direct mapping (simple):
// $button-padding: 8px 16px;
// (Define directly in bridge)
//
// Approach B - Reference tokens (recommended):
// @use 'components' as comp;
// $button-padding: comp.$button-padding;
// (Map from your component tokens)
//
// We use Approach B because:
// → Single source of truth (component tokens)
// → Change tokens, Quasar updates automatically
// → Consistency between custom components and Quasar components
//
// Bridge layer: Maps your tokens to Quasar's variable names
// This file overrides Quasar defaults with your design tokens
@use "components" as comp;
@use "decisions" as dec;
// Override Quasar's button variables with your component tokens
// Pattern: $quasar-var-name: comp.$your-token-name
$button-border-radius: comp.$button-radius !default;
$button-padding: comp.$button-padding !default;
$button-dense-padding: comp.$button-padding-dense !default;
$button-fab-icon-font-size: comp.$button-fab-icon-size !default;
$button-transition: comp.$button-transition !default;
$button-font-size: comp.$button-font-size !default;
$button-line-height: comp.$button-line-height !default;
$button-font-weight: comp.$button-font-weight !default;
$button-shadow: comp.$button-shadow !default;
$button-shadow-active: comp.$button-shadow-active !default;
$button-rounded-border-radius: comp.$button-radius-rounded !default;
$button-push-border-radius: comp.$button-radius-push !default;
// Override Quasar's color variables
$primary: dec.$color-primary !default;
$secondary: dec.$color-secondary !default;
// Override spacing (if Quasar uses $space-base)
$space-base: dec.$spacing-md !default;
//
// WHAT GETS OVERRIDDEN?
// ---------------------
// When Quasar sees: $button-padding !default;
// It checks: "Is $button-padding already defined?"
// → YES (we defined it above) → Use our value ✓
// → NO → Use Quasar's default
//
// RESULT: All Quasar buttons automatically use your design tokens!
// ----------------------------------------------------------------------------
// 02-tokens/_index.scss (Public API)
// ----------------------------------------------------------------------------
// THE GATEKEEPER: Controls What Developers Can Access
//
// PURPOSE: Define the PUBLIC API of your token system
// THINK OF IT AS: The "exports" of your token module
//
// HOW IT WORKS:
// @forward 'decisions'; ← Makes everything from decisions.scss available
// @forward 'components'; ← Makes everything from components.scss available
// (no @forward for 'options') ← Options stay PRIVATE/hidden
//
// WHY THIS PATTERN?
// 1. ENCAPSULATION: Hide implementation details (options)
// 2. BETTER DX: Developers only see what they should use
// 3. SMALLER BUNDLES: Unused options don't get compiled
// 4. NON-BREAKING CHANGES: Change options without breaking developer code
//
// WHAT DOES @forward DO?
// When you @use '02-tokens', you get access to everything @forwarded here
// It's like saying "here's what's public, everything else is private"
//
// ANALOGY:
// Think of a restaurant:
// - Kitchen (options) = Private, customers don't see it
// - Menu (decisions/components) = Public, this is what customers use
// - _index.scss = The menu board that lists what's available
//
// SCSS MODULES PRIMER:
// @use 'file' as name; → Import for use in THIS file only
// @forward 'file'; → Re-export for others who import THIS file
// @import 'file'; → Old way (global namespace, avoid in new code)
@forward "decisions";
@forward "components";
// Note: 'options' is NOT forwarded - it's private/internal only
// ============================================================================
// 05-COMPONENTS LAYER (Atomic Design Structure)
// ============================================================================
// ----------------------------------------------------------------------------
// 05-components/_atoms/_button.scss
// ----------------------------------------------------------------------------
// Custom button using BEM + Design Tokens
// This demonstrates building components alongside Quasar
@use "../../02-tokens" as tokens;
.btn {
// Base button styles using component tokens
display: inline-flex;
align-items: center;
justify-content: center;
padding: tokens.$button-padding;
border-radius: tokens.$button-radius;
font-size: tokens.$button-font-size;
line-height: tokens.$button-line-height;
font-weight: tokens.$button-font-weight;
transition: tokens.$button-transition;
cursor: pointer;
border: none;
text-decoration: none;
user-select: none;
// Variants (BEM modifiers)
&--primary {
background-color: tokens.$button-primary-bg;
color: tokens.$button-primary-text;
box-shadow: tokens.$button-shadow;
&:hover {
background-color: tokens.$button-primary-bg-hover;
box-shadow: tokens.$button-shadow-active;
}
&:active {
box-shadow: tokens.$button-shadow;
}
}
&--secondary {
background-color: tokens.$button-secondary-bg;
color: tokens.$button-secondary-text;
box-shadow: tokens.$button-shadow;
&:hover {
opacity: 0.9;
box-shadow: tokens.$button-shadow-active;
}
}
&--outline {
background-color: transparent;
color: tokens.$button-outline-text;
border: 2px solid tokens.$button-outline-border;
box-shadow: none;
&:hover {
background-color: tokens.$button-outline-bg-hover;
}
}
&--rounded {
border-radius: tokens.$button-radius-rounded;
}
&--dense {
padding: tokens.$button-padding-dense;
}
&--block {
display: flex;
width: 100%;
}
// States
&:disabled,
&--disabled {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none;
}
// Elements (BEM elements)
&__icon {
&--left {
margin-right: tokens.$spacing-sm;
}
&--right {
margin-left: tokens.$spacing-sm;
}
}
&__label {
flex: 1;
}
}
// ----------------------------------------------------------------------------
// 05-components/_molecules/_card.scss
// ----------------------------------------------------------------------------
// Custom card component using BEM + Design Tokens
@use "../../02-tokens" as tokens;
.card {
background-color: tokens.$card-bg;
border-radius: tokens.$card-radius;
box-shadow: tokens.$card-shadow;
overflow: hidden;
// Elements
&__header {
padding: tokens.$card-header-padding;
border-bottom: 1px solid tokens.$color-surface-variant;
}
&__title {
margin: 0;
font-size: tokens.$text-body-size;
font-weight: tokens.$text-weight-emphasis;
color: tokens.$color-on-surface;
}
&__body {
padding: tokens.$card-body-padding;
}
&__footer {
padding: tokens.$card-footer-padding;
border-top: 1px solid tokens.$color-surface-variant;
display: flex;
justify-content: flex-end;
gap: tokens.$spacing-sm;
}
// Modifiers
&--flat {
box-shadow: none;
border: 1px solid tokens.$color-surface-variant;
}
&--bordered {
border: 1px solid tokens.$color-surface-variant;
}
}
// ----------------------------------------------------------------------------
// 05-components/_quasar-overrides/_q-btn-custom.scss
// ----------------------------------------------------------------------------
// Additional customization for Quasar's q-btn component
// This goes beyond variable overrides for specific use cases
@use "../../02-tokens" as tokens;
.q-btn {
// Add custom variant that Quasar doesn't have
&.q-btn--gradient-primary {
background: linear-gradient(
135deg,
tokens.$button-primary-bg 0%,
tokens.$button-primary-bg-hover 100%
);
color: tokens.$button-primary-text;
}
// Custom size variant
&.q-btn--xs {
font-size: 12px;
padding: tokens.$spacing-xs tokens.$spacing-sm;
}
// Industry-specific variant (e.g., for your domain)
&.q-btn--action {
text-transform: none;
letter-spacing: 0.5px;
font-weight: tokens.$font-weight-bold;
}
}
// ============================================================================
// MAIN.SCSS - Putting it all together
// ============================================================================
//
// THE COMPLETE IMPORT STRATEGY
// =============================
//
// CRITICAL SECTION: Understanding Import Order & CSS Layers
//
// PART 1: SCSS IMPORT ORDER (for variable overrides)
// ---------------------------------------------------
// This happens at COMPILE TIME (before CSS is generated)
//
// Step 1: Import Quasar bridge BEFORE Quasar itself
// ↓
@use "02-tokens/quasar-bridge";
// ↑ This defines variables like $button-padding
//
// Step 2: Import Quasar (it will use your overridden variables)
// ↓
@import "quasar/src/css/index.sass";
// ↑ Quasar reads $button-padding and uses YOUR value
// ↑ Why @import here? Quasar uses old syntax, we must too
//
// 💡 KEY INSIGHT:
// The ORDER matters because SCSS processes files sequentially
// If you import Quasar BEFORE bridge, it uses defaults (bridge is too late!)
//
//
// PART 2: CSS LAYERS (for cascade control)
// -----------------------------------------
// This happens at RUNTIME (in the browser)
//
// Step 3: Define CSS layers for proper cascade control
// ↓
@layer reset, tokens, layout, quasar-overrides, components, utilities;
//
// WHAT ARE CSS LAYERS? (CSS feature, not SCSS)
// CSS Layers control specificity without using !important
// Think of them as "priority buckets" for your styles
//
// WHY USE LAYERS?
// Without layers:
// .button { color: blue; } ← Specificity: 0,1,0
// .btn { color: red; } ← Specificity: 0,1,0
// → Last one wins (source order matters, fragile!)
//
// With layers:
// @layer base { .button { color: blue; } }
// @layer components { .btn { color: red; } }
// → "components" layer wins (explicit priority, predictable!)
//
// LAYER ORDER = PRIORITY (Lower → Higher)
// ┌─────────────────────────────────────────────────────────────┐
// │ 1. reset ← Lowest priority (normalize.css, etc.) │
// │ 2. tokens ← CSS custom properties (if needed) │
// │ 3. layout ← Grid, containers, structure │
// │ 4. quasar-overrides ← Modify Quasar components │
// │ 5. components ← Your custom components (BEM) │
// │ 6. utilities ← Highest priority (spacing, text utils) │
// └─────────────────────────────────────────────────────────────┘
//
// EXAMPLE IN ACTION:
// Quasar button has: .q-btn { padding: 8px }
// Your layer has: @layer components { .q-btn { padding: 12px } }
// → Your padding wins because "components" layer is higher priority!
//
// HOW IT RELATES TO YOUR ITCSS:
// Your structure: CSS Layer:
// 01-reset → @layer reset
// 02-tokens → @layer tokens
// 03-layout → @layer layout
// 04-utils → @layer utilities
// 05-components → @layer components
//
// WHY quasar-overrides LAYER?
// → Sometimes you need to modify Quasar components beyond variables
// → This layer sits between layout and your components
// → Gives you surgical control over Quasar without !important
@layer reset {
@import "01-reset/normalize";
}
@layer tokens {
// Generate CSS custom properties from tokens if needed
// Example: :root { --color-primary: #1976d2; }
// This is optional - useful for runtime theme switching
@import "02-tokens/css-properties"; // Optional
}
@layer layout {
@import "03-layout/grid";
@import "03-layout/container";
}
@layer quasar-overrides {
// Additional Quasar customizations beyond variable overrides
@import "05-components/quasar-overrides/q-btn-custom";
}
@layer components {
// Your custom components (Atomic Design structure)
@import "05-components/atoms/button";
@import "05-components/molecules/card";
// ... more components
}
@layer utilities {
@import "04-utils/spacing";
@import "04-utils/text";
}
//
// COMPLETE MENTAL MODEL:
// =====================
//
// COMPILE TIME (SCSS):
// 1. Bridge defines variables
// 2. Quasar reads those variables
// 3. Everything compiles to CSS
//
// RUNTIME (Browser):
// 1. Reset styles apply (lowest priority)
// 2. Tokens/layout provide structure
// 3. Quasar components render
// 4. Your overrides apply (if needed)
// 5. Your custom components work alongside Quasar
// 6. Utilities can override anything (highest priority)
//
// RESULT:
// ✓ Quasar uses your design tokens automatically
// ✓ You can override Quasar when needed
// ✓ Your custom components stay consistent
// ✓ Predictable cascade (no specificity wars!)
//
// ============================================================================
// USAGE EXAMPLES
// ============================================================================
/*
<!-- Using Quasar button (automatically uses your tokens) -->
<q-btn
color="primary"
label="Click Me"
/>
<!-- Using Quasar button with custom override -->
<q-btn
class="q-btn--gradient-primary"
label="Gradient Button"
/>
<!-- Using your custom BEM button -->
<button class="btn btn--primary">
<span class="btn__icon btn__icon--left">🚀</span>
<span class="btn__label">Launch</span>
</button>
<!-- Using your custom card -->
<div class="card">
<div class="card__header">
<h3 class="card__title">Card Title</h3>
</div>
<div class="card__body">
Card content goes here
</div>
<div class="card__footer">
<button class="btn btn--outline">Cancel</button>
<button class="btn btn--primary">Save</button>
</div>
</div>
<!-- Mixing Quasar and custom components -->
<div class="card">
<div class="card__body">
<q-input label="Name" />
</div>
<div class="card__footer">
<q-btn flat label="Cancel" />
<q-btn color="primary" label="Submit" />
</div>
</div>
*/