This monorepo uses SCSS with Vite and Turborepo for styling, replacing CSS-in-JS for better performance and zero runtime overhead.
- Build Tool: Turborepo (orchestration) + Vite (bundling)
- SCSS Compiler: Dart Sass (via Vite)
- PostCSS: Autoprefixer for vendor prefixes
- Module Resolution: Vite aliases for
@mining-sdk/*workspace packages
✅ Already in use for the monorepo
✅ Intelligent caching and parallel builds
✅ Dependency graph management
✅ Perfect for monorepos with multiple packages
✅ Fast builds with esbuild
✅ Built-in SCSS support
✅ Modern, well-maintained
✅ Easy configuration
✅ HMR support for development
| Tool | Pros | Cons |
|---|---|---|
| Vite (Current) | Fast, modern, built-in SCSS | - |
| Webpack | Mature, flexible | Slow, complex config |
| Rollup | Small bundles | Requires more plugins |
| Custom Script | Full control | Maintenance burden |
| Nx | Powerful | Overkill for this use case |
| Bazel | Enterprise-grade | Too complex |
┌─────────────────────────────────────┐
│ Turborepo (Orchestration) │
│ - Manages build order │
│ - Caches build outputs │
│ - Runs tasks in parallel │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Vite (Build Tool) │
│ - Compiles SCSS to CSS │
│ - Resolves @mining-sdk/* imports │
│ - Applies PostCSS transforms │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Output: styles.css │
│ - Minified, optimized │
│ - Autoprefixed │
│ - Ready for production │
└─────────────────────────────────────┘
- @mining-sdk/core - Core components with base styles and design tokens
- @mining-sdk/foundation - Foundation components and feature styles
- @mining-sdk/fonts - Font assets (JetBrains Mono)
Packages with SCSS build:
-
@mining-sdk/coresrc/styles/- Source SCSS filesdist/styles.css- Compiled, minified CSSvite.config.js- Vite configuration for SCSS compilation
-
@mining-sdk/foundationsrc/styles/- Source SCSS filesdist/styles.css- Compiled CSSvite.config.js- Vite configuration
-
@mining-sdk/fontssrc/- Font files and CSSdist/jetbrains-mono.css- Built font CSSvite.config.js- Vite configuration
import { defineConfig } from 'vite'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'src/styles.scss'),
formats: ['es'],
},
outDir: resolve(__dirname, 'src'),
emptyOutDir: false,
cssCodeSplit: false,
rollupOptions: {
output: {
assetFileNames: 'styles.css',
},
},
},
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
loadPaths: [resolve(__dirname, '../')],
},
},
},
resolve: {
alias: {
'@mining-sdk/core': resolve(__dirname, '../core/src'),
'@mining-sdk/theme': resolve(__dirname, '../theme/src'),
},
},
}){
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", "src/styles.css"]
},
"build:scss": {
"dependsOn": ["^build:scss"],
"outputs": ["src/styles.css"]
}
}
}// packages/my-package/src/styles/index.scss
/**
* @mining-sdk/my-package styles
*/
// Import from other workspace packages (core exports SCSS mixins)
@use '@mining-sdk/core/styles' as core;
// Your styles using CSS variables
.my-component {
background: hsl(var(--background));
color: hsl(var(--foreground));
padding: 1rem;
// Use core mixins if available
// @include core.flex-center;
}// packages/my-package/vite.config.js
import { defineConfig } from 'vite'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'src/styles/index.scss'),
formats: ['es'],
},
outDir: resolve(__dirname, 'dist'),
emptyOutDir: false,
cssCodeSplit: false,
rollupOptions: {
output: {
assetFileNames: 'styles.css',
},
},
},
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
loadPaths: [resolve(__dirname, '../')],
},
},
},
resolve: {
alias: {
'@mining-sdk/core': resolve(__dirname, '../core/src'),
'@mining-sdk/foundation': resolve(__dirname, '../foundation/src'),
},
},
})Update package.json:
{
"scripts": {
"build": "pnpm build:ts && pnpm build:scss",
"build:scss": "vite build",
"build:ts": "tsc"
}
}Update package.json exports:
{
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
},
"./styles.css": "./src/styles.css"
}
}# Build everything (TypeScript + SCSS)
pnpm build
# Build only SCSS across all packages
pnpm build:scss
# Build specific package
pnpm --filter @mining-sdk/core build
pnpm --filter @mining-sdk/core build:scss# Build with cache
pnpm build
# → Uses cached results if nothing changed
# Force rebuild (no cache)
pnpm build --force
# Build with dependencies
pnpm --filter @mining-sdk/components-foundation... build
# → Builds foundation + all its dependencies
# Parallel builds
# → Turborepo automatically parallelizes independent builds// In your app entry point (e.g., apps/demo/src/main.tsx)
import '@mining-sdk/core/styles.css'
import '@mining-sdk/foundation/styles.css'
import '@mining-sdk/fonts/jetbrains-mono.css'Note: Always import compiled CSS files (.css), not SCSS source files. The SCSS is pre-compiled during the build process.
Vite resolves @mining-sdk/* imports using aliases:
// This works! ✅
@use '@mining-sdk/core/styles' as core;
// No need for relative paths ❌
@use '../../core/src/styles/_mixins.scss' as core;- Vite Alias: Maps
@mining-sdk/coreto../core/src - SCSS Load Paths: Adds
../to SCSS resolution - Modern SCSS API: Uses Dart Sass modern compiler
// Example: packages/core/src/styles/_variables.scss (if exists)
$spacing-unit: 0.25rem;
$border-radius-base: 0.5rem;
// Usage in other SCSS files
.card {
padding: $spacing-unit * 4; // 1rem
border-radius: $border-radius-base;
}
// Better: Use CSS variables for runtime theming
.card {
padding: 1rem;
border-radius: var(--radius);
}// Example mixin (if defined in core)
@mixin theme-colors($mode: 'light') {
@if $mode == 'light' {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
} @else {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
}
}
// Usage in your SCSS
:root {
@include theme-colors('light');
}
.dark {
@include theme-colors('dark');
}.card {
padding: 1rem;
&-header {
font-weight: bold;
}
&-body {
margin-top: 0.5rem;
}
&:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}# First build
pnpm build
# → Takes ~5s
# Second build (no changes)
pnpm build
# → Takes ~0.1s (cached!)
# Change one package
pnpm build
# → Only rebuilds changed package + dependents| Feature | CSS-in-JS | SCSS + Vite |
|---|---|---|
| Runtime overhead | ❌ ~5-10kb | ✅ Zero |
| Build time | ✅ Fast (Vite) | |
| Bundle size | ❌ Larger | ✅ Smaller |
| Caching | ❌ Harder | ✅ Turborepo |
| HMR | ✅ Good | ✅ Excellent |
| SSR | ❌ Complex | ✅ Simple |
Error: Can't find stylesheet to import
Solution: Check Vite alias configuration:
resolve: {
alias: {
'@mining-sdk/core': resolve(__dirname, '../core/src'),
},
}Error: Cannot find module 'vite'
Solution: Install dependencies:
pnpm installError: Stale build output
Solution: Clear Turborepo cache:
pnpm clean
pnpm build --force// ✅ Good - Uses CSS variables
.button {
background: hsl(var(--primary));
color: hsl(var(--primary-foreground));
}
// ❌ Bad - Hardcoded colors
.button {
background: #1a1a1a;
color: #ffffff;
}// ✅ Good - Namespaced
@use '@mining-sdk/core/styles' as core;
.my-component {
// Use core mixins if available
// @include core.flex-center;
}
// ❌ Bad - Global namespace pollution
@use '@mining-sdk/core/styles' as *;# ✅ Good - Let Turborepo handle dependencies
pnpm build
# ❌ Bad - Manual dependency management
pnpm --filter @mining-sdk/core build
pnpm --filter @mining-sdk/foundation build
pnpm --filter @mining-sdk/fonts build// ✅ Good - Low specificity
.card {
padding: 1rem;
}
// ❌ Bad - High specificity
div.container .card-wrapper .card {
padding: 1rem;
}- Turborepo: Simpler, faster for most use cases
- Nx: More features, but more complex
- Our Choice: Turborepo is sufficient and already in use
- Bazel: Enterprise-grade, very complex
- Turborepo: Easier to learn and maintain
- Our Choice: Turborepo provides 80% of benefits with 20% of complexity
- Custom: Full control, maintenance burden
- Vite: Battle-tested, community support
- Our Choice: Vite is standard and well-maintained
- Add CSS minification for production
- Generate TypeScript types for CSS modules
- Add SCSS linting rules
- Implement watch mode for development
- Add source maps for debugging