diff --git a/PRD.md b/PRD.md
index b1c2ed3..a540f71 100644
--- a/PRD.md
+++ b/PRD.md
@@ -96,6 +96,13 @@ This is a full-featured low-code IDE with multiple integrated tools (code editor
- **Progression**: User finalizes design → Clicks export → System bundles files → Downloads zip or shows code → User extracts and runs locally
- **Success criteria**: Generated project structure is valid; includes package.json; code runs without errors
+### Custom Sass Styling System
+- **Functionality**: Comprehensive Sass-based styling system for non-standard Material UI components with pre-built components, utilities, mixins, and animations
+- **Purpose**: Extend Material UI with custom styled components and provide consistent design patterns through Sass
+- **Trigger**: Using Sass classes in components or importing Sass modules for custom styling
+- **Progression**: Import main.scss → Apply custom component classes → Use mixins for custom styles → Configure variables for theming
+- **Success criteria**: All Sass styles compile correctly; custom components render with proper styling; mixins work as expected; animations are smooth and respect reduced motion preferences
+
## Edge Case Handling
- **Empty Projects**: Show welcome screen with quick-start templates when no project exists; AI can generate entire projects from scratch
- **Invalid Prisma Schemas**: Validate models and show inline errors before generating code
diff --git a/README.md b/README.md
index 1e75a06..6745922 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ A comprehensive visual low-code platform for generating production-ready Next.js
- **Prisma Schema Designer** - Visual database model builder with relations and field configuration
- **Component Tree Builder** - Hierarchical React component designer with Material UI integration
- **Theme Designer** - Advanced theming with multiple variants (light/dark/custom) and unlimited custom colors
+- **Sass Styling System** - Custom Material UI components with Sass, including utilities, mixins, and animations
- **Flask Backend Designer** - Python REST API designer with blueprints, endpoints, and CORS configuration
- **Project Settings** - Configure Next.js options, npm packages, scripts, and build settings
@@ -58,6 +59,7 @@ A comprehensive visual low-code platform for generating production-ready Next.js
- Next.js 14 with App Router
- React 18 with TypeScript
- Material UI 5
+- Sass/SCSS for custom styling
- Monaco Editor
- Tailwind CSS
- Framer Motion
@@ -81,6 +83,7 @@ The application includes comprehensive built-in documentation:
- **README** - Complete feature overview and getting started guide
- **Roadmap** - Completed features and planned enhancements
- **Agents Files** - AI service architecture and integration points
+- **Sass Styles Guide** - Custom Material UI components, utilities, mixins, and animations
Access documentation by clicking the **Documentation** tab in the application.
@@ -95,6 +98,7 @@ Access documentation by clicking the **Documentation** tab in the application.
- Auto error detection and repair
- Flask backend designer
- Project settings and npm management
+- Custom Sass styling system with utilities and mixins
### 🔮 Planned
- Real-time preview with hot reload
diff --git a/package-lock.json b/package-lock.json
index e5a1a68..4d7c58d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -63,6 +63,7 @@
"react-hook-form": "^7.54.2",
"react-resizable-panels": "^2.1.7",
"recharts": "^2.15.1",
+ "sass": "^1.97.2",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"three": "^0.175.0",
@@ -1479,6 +1480,302 @@
"node": ">= 18"
}
},
+ "node_modules/@parcel/watcher": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.4.tgz",
+ "integrity": "sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^2.0.3",
+ "is-glob": "^4.0.3",
+ "node-addon-api": "^7.0.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher-android-arm64": "2.5.4",
+ "@parcel/watcher-darwin-arm64": "2.5.4",
+ "@parcel/watcher-darwin-x64": "2.5.4",
+ "@parcel/watcher-freebsd-x64": "2.5.4",
+ "@parcel/watcher-linux-arm-glibc": "2.5.4",
+ "@parcel/watcher-linux-arm-musl": "2.5.4",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.4",
+ "@parcel/watcher-linux-arm64-musl": "2.5.4",
+ "@parcel/watcher-linux-x64-glibc": "2.5.4",
+ "@parcel/watcher-linux-x64-musl": "2.5.4",
+ "@parcel/watcher-win32-arm64": "2.5.4",
+ "@parcel/watcher-win32-ia32": "2.5.4",
+ "@parcel/watcher-win32-x64": "2.5.4"
+ }
+ },
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.4.tgz",
+ "integrity": "sha512-hoh0vx4v+b3BNI7Cjoy2/B0ARqcwVNrzN/n7DLq9ZB4I3lrsvhrkCViJyfTj/Qi5xM9YFiH4AmHGK6pgH1ss7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.4.tgz",
+ "integrity": "sha512-kphKy377pZiWpAOyTgQYPE5/XEKVMaj6VUjKT5VkNyUJlr2qZAn8gIc7CPzx+kbhvqHDT9d7EqdOqRXT6vk0zw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.4.tgz",
+ "integrity": "sha512-UKaQFhCtNJW1A9YyVz3Ju7ydf6QgrpNQfRZ35wNKUhTQ3dxJ/3MULXN5JN/0Z80V/KUBDGa3RZaKq1EQT2a2gg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.4.tgz",
+ "integrity": "sha512-Dib0Wv3Ow/m2/ttvLdeI2DBXloO7t3Z0oCp4bAb2aqyqOjKPPGrg10pMJJAQ7tt8P4V2rwYwywkDhUia/FgS+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.4.tgz",
+ "integrity": "sha512-I5Vb769pdf7Q7Sf4KNy8Pogl/URRCKu9ImMmnVKYayhynuyGYMzuI4UOWnegQNa2sGpsPSbzDsqbHNMyeyPCgw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.4.tgz",
+ "integrity": "sha512-kGO8RPvVrcAotV4QcWh8kZuHr9bXi9a3bSZw7kFarYR0+fGliU7hd/zevhjw8fnvIKG3J9EO5G6sXNGCSNMYPQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.4.tgz",
+ "integrity": "sha512-KU75aooXhqGFY2W5/p8DYYHt4hrjHZod8AhcGAmhzPn/etTa+lYCDB2b1sJy3sWJ8ahFVTdy+EbqSBvMx3iFlw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.4.tgz",
+ "integrity": "sha512-Qx8uNiIekVutnzbVdrgSanM+cbpDD3boB1f8vMtnuG5Zau4/bdDbXyKwIn0ToqFhIuob73bcxV9NwRm04/hzHQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.4.tgz",
+ "integrity": "sha512-UYBQvhYmgAv61LNUn24qGQdjtycFBKSK3EXr72DbJqX9aaLbtCOO8+1SkKhD/GNiJ97ExgcHBrukcYhVjrnogA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.4.tgz",
+ "integrity": "sha512-YoRWCVgxv8akZrMhdyVi6/TyoeeMkQ0PGGOf2E4omODrvd1wxniXP+DBynKoHryStks7l+fDAMUBRzqNHrVOpg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.4.tgz",
+ "integrity": "sha512-iby+D/YNXWkiQNYcIhg8P5hSjzXEHaQrk2SLrWOUD7VeC4Ohu0WQvmV+HDJokZVJ2UjJ4AGXW3bx7Lls9Ln4TQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.4.tgz",
+ "integrity": "sha512-vQN+KIReG0a2ZDpVv8cgddlf67J8hk1WfZMMP7sMeZmJRSmEax5xNDNWKdgqSe2brOKTQQAs3aCCUal2qBHAyg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.4.tgz",
+ "integrity": "sha512-3A6efb6BOKwyw7yk9ro2vus2YTt2nvcd56AuzxdMiVOxL9umDyN5PKkKfZ/gZ9row41SjVmTVQNWQhaRRGpOKw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
"node_modules/@phosphor-icons/react": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz",
@@ -5247,6 +5544,21 @@
"node": ">= 16"
}
},
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/class-variance-authority": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
@@ -7238,6 +7550,12 @@
"node": ">= 4"
}
},
+ "node_modules/immutable": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
+ "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
+ "license": "MIT"
+ },
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -7329,7 +7647,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -7339,7 +7657,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -8084,6 +8402,13 @@
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
}
},
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/nwsapi": {
"version": "2.2.23",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
@@ -8733,6 +9058,19 @@
"react-dom": ">=16.6.0"
}
},
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/recharts": {
"version": "2.15.4",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz",
@@ -8987,6 +9325,26 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
+ "node_modules/sass": {
+ "version": "1.97.2",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.2.tgz",
+ "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==",
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "immutable": "^5.0.2",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher": "^2.4.1"
+ }
+ },
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
diff --git a/package.json b/package.json
index 6fac6a5..c4fea5c 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
"react-hook-form": "^7.54.2",
"react-resizable-panels": "^2.1.7",
"recharts": "^2.15.1",
+ "sass": "^1.97.2",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"three": "^0.175.0",
diff --git a/src/App.tsx b/src/App.tsx
index 7cb01e0..f1b4b07 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -20,6 +20,7 @@ import { FlaskDesigner } from '@/components/FlaskDesigner'
import { ProjectSettingsDesigner } from '@/components/ProjectSettingsDesigner'
import { ErrorPanel } from '@/components/ErrorPanel'
import { DocumentationView } from '@/components/DocumentationView'
+import { SassStylesShowcase } from '@/components/SassStylesShowcase'
import { generateNextJSProject, generatePrismaSchema, generateMUITheme, generatePlaywrightTests, generateStorybookStories, generateUnitTests, generateFlaskApp } from '@/lib/generators'
import { AIService } from '@/lib/ai-service'
import { toast } from 'sonner'
@@ -350,6 +351,10 @@ function App() {
Documentation
+
+
+ Sass Styles
+
@@ -428,6 +433,10 @@ function App() {
+
+
+
+
diff --git a/src/components/DocumentationView.tsx b/src/components/DocumentationView.tsx
index 90667b0..14cd82f 100644
--- a/src/components/DocumentationView.tsx
+++ b/src/components/DocumentationView.tsx
@@ -46,6 +46,10 @@ export function DocumentationView() {
Agents Files
+
+
+ Sass Styles Guide
+
@@ -728,6 +732,387 @@ export function DocumentationView() {
+
+
+
+
+
+
+
Sass Styles Guide
+
+ Custom Material UI components with Sass
+
+
+
+
+
+
+
+
Overview
+
+ CodeForge includes a comprehensive Sass-based styling system for non-standard Material UI components.
+ This system provides pre-built components, utilities, mixins, and animations that extend beyond the
+ standard Material UI component library.
+
+
+
+
+
+ File Structure
+
+
+
+
src/styles/_variables.scss
+
+ Color palettes, spacing scales, typography, transitions, and other design tokens
+
+
+
+
src/styles/_utilities.scss
+
+ Mixins and functions for responsive design, colors, typography, and layout helpers
+
+
+
+
src/styles/_animations.scss
+
+ Keyframe animations and animation utility classes for transitions and effects
+
+
+
+
src/styles/material-ui-custom.scss
+
+ Custom Material UI component styles with variants and states
+
+
+
+
src/styles/main.scss
+
+ Main entry point that imports all Sass modules and provides layout components
+
+
+
+
+
+
+
+ Available Components
+ Custom Material UI components built with Sass
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Layout Components
+ Sass-powered layout utilities
+
+
+ }
+ title="custom-mui-container"
+ description="Max-width container with responsive padding"
+ />
+ }
+ title="custom-mui-grid"
+ description="CSS Grid layouts with responsive columns (--cols-1 to --cols-12, --responsive)"
+ />
+ }
+ title="custom-mui-flex"
+ description="Flexbox utilities (--row, --col, --wrap, --center, --between, --around)"
+ />
+ }
+ title="custom-mui-stack"
+ description="Vertical/horizontal stacks with configurable gaps"
+ />
+ }
+ title="custom-mui-surface"
+ description="Interactive surfaces with elevation and hover effects"
+ />
+
+
+
+
+
+ Sass Utilities & Mixins
+ Reusable functions for custom styling
+
+
+
+
+
+
+ Responsive Design
+
+
+
@include respond-to($breakpoint)
+
Generate media queries for xs, sm, md, lg, xl, 2xl breakpoints
+
+{`@include respond-to('lg') {
+ padding: 2rem;
+}`}
+
+
+
+
+
+
+
+
+
+ Elevation & Shadows
+
+
+
@include elevation($level)
+
Apply box shadows with levels 1-4
+
+{`@include elevation(2);`}
+
+
+
+
+
+
+
+
+
+ Glassmorphism
+
+
+
@include glassmorphism($blur, $opacity)
+
Create frosted glass effects with backdrop blur
+
+{`@include glassmorphism(16px, 0.1);`}
+
+
+
+
+
+
+
+
+
+ Color Functions
+
+
+
get-color($palette, $shade)
+
Access colors from predefined palettes (primary, secondary, accent, success, error, warning)
+
+{`color: get-color('primary', 500);`}
+
+
+
+
+
+
+
+
+
+ Text Truncation
+
+
+
@include truncate($lines)
+
Truncate text with ellipsis after specified lines
+
+{`@include truncate(2);`}
+
+
+
+
+
+
+
+
+
+ Custom Scrollbars
+
+
+
@include show-scrollbar($track, $thumb)
+
Style webkit scrollbars with custom colors
+
+{`@include show-scrollbar(rgba(0,0,0,0.1), rgba(0,0,0,0.3));`}
+
+
+
+
+
+
+
+
+
+ Animation Classes
+ Pre-built animation utilities
+
+
+
+
+
+
+
+
+
+
+ Quick Start Example
+
+
+
+
+
Using Custom Components
+
+{`import './styles/main.scss'
+
+function MyComponent() {
+ return (
+
+
+
+
Card Title
+
Card content
+
+ Click Me
+
+
+
+
+ )
+}`}
+
+
+
+
+
+
+
Creating Custom Styles with Mixins
+
+{`@use './styles/utilities' as *;
+@use './styles/variables' as *;
+
+.my-custom-component {
+ @include elevation(2);
+ @include responsive-padding(spacing('6'));
+ background: get-color('primary', 500);
+
+ @include respond-to('md') {
+ @include elevation(3);
+ }
+
+ &:hover {
+ @include glassmorphism(12px, 0.15);
+ }
+}`}
+
+
+
+
+
+
+
+
+
+ Best Practices
+
+
+
+
+
+
+ Import main.scss in your index.css to access all Sass components and utilities
+
+
+
+ Use @use instead of @import for better module encapsulation
+
+
+
+ Leverage mixins for consistent spacing, elevation, and responsive design
+
+
+
+ Extend existing component classes rather than creating from scratch
+
+
+
+ Use animation classes sparingly and respect prefers-reduced-motion
+
+
+
+ Customize variables in _variables.scss to match your design system
+
+
+
+
+
+
@@ -735,6 +1120,29 @@ export function DocumentationView() {
)
}
+function SassComponentItem({ name, classes, description }: { name: string; classes: string[]; description: string }) {
+ return (
+
+
{name}
+
{description}
+
+ {classes.map((cls, idx) => (
+ {cls}
+ ))}
+
+
+ )
+}
+
+function AnimationItem({ name, description }: { name: string; description: string }) {
+ return (
+
+
{name}
+
{description}
+
+ )
+}
+
function FeatureItem({ icon, title, description }: { icon: React.ReactNode; title: string; description: string }) {
return (
diff --git a/src/components/SassStylesShowcase.tsx b/src/components/SassStylesShowcase.tsx
new file mode 100644
index 0000000..d6535db
--- /dev/null
+++ b/src/components/SassStylesShowcase.tsx
@@ -0,0 +1,320 @@
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+import { ScrollArea } from '@/components/ui/scroll-area'
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
+import { Badge } from '@/components/ui/badge'
+import { Button } from '@/components/ui/button'
+import { Code, Palette, Sparkle, CheckCircle } from '@phosphor-icons/react'
+
+export function SassStylesShowcase() {
+ return (
+
+
+
Custom Material UI Sass Styles
+
+ Non-standard Material UI CSS components built with Sass
+
+
+
+
+
+ Buttons
+ Inputs
+ Cards
+ Chips
+ Layout
+ Animations
+
+
+
+
+
+ Custom Button Styles
+
+
+
+
+
+ Primary Button
+
+
+ Secondary Button
+
+
+ Accent Button
+
+
+
+
+
+ Outline Button
+
+
+ Ghost Button
+
+
+
+
+
+
+{`
+ Primary Button
+
+
+
+ Accent Button
+ `}
+
+
+
+
+
+
+
+
+
+ Custom Input Styles
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Custom Card Styles
+
+
+
+
+
Standard Card
+
+ Basic card with elevation and hover effect
+
+
+
+
+
Gradient Card
+
+ Card with gradient background
+
+
+
+
+
Glass Card
+
+ Glassmorphism effect card
+
+
+
+
+
+
+{`
+ Standard Card
+
+
+
+ Gradient Card
+
+
+
+ Glass Card
+
`}
+
+
+
+
+
+
+
+
+
+ Custom Chip/Badge Styles
+
+
+
+
+ Primary
+
+
+ Secondary
+
+
+ Accent
+
+
+
+ Success
+
+
+ Error
+
+
+ Warning
+
+
+
+
+
+{`
+ Primary
+
+
+
+
+ Success
+ `}
+
+
+
+
+
+
+
+ Custom Tags
+
+
+
+ Default
+ Small
+ Large
+
+
+
+
+
+
+
+
+ Custom Layout Components
+
+
+
+
Custom Stack
+
+
Item 1
+
Item 2
+
Item 3
+
+
+
+
+
Custom Grid (Responsive)
+
+
Grid 1
+
Grid 2
+
Grid 3
+
Grid 4
+
+
+
+
+
Custom Surface
+
+
Interactive surface with hover effects
+
+
+
+
+
+
+
+
+
+
+
+ Animation Classes
+
+
+
+
+ Fade In
+
+
+ Slide Up
+
+
+ Scale In
+
+
+ Pulse
+
+
+ Bounce
+
+
+ Float
+
+
+
+
+
+{`Fade In
+Slide Up
+Pulse
+Bounce
`}
+
+
+
+
+
+
+
+ Skeleton Loading
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/index.css b/src/index.css
index 11489c6..b4f6481 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,5 +1,6 @@
@import 'tailwindcss';
@import "tw-animate-css";
+@import './styles/main.scss';
@layer base {
* {
diff --git a/src/styles/_animations.scss b/src/styles/_animations.scss
new file mode 100644
index 0000000..e1b0ded
--- /dev/null
+++ b/src/styles/_animations.scss
@@ -0,0 +1,364 @@
+@use './variables' as *;
+
+@keyframes fade-in {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes fade-out {
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+@keyframes slide-in-up {
+ from {
+ transform: translateY(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+@keyframes slide-in-down {
+ from {
+ transform: translateY(-100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+@keyframes slide-in-left {
+ from {
+ transform: translateX(-100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+@keyframes slide-in-right {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+@keyframes scale-in {
+ from {
+ transform: scale(0.95);
+ opacity: 0;
+ }
+ to {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+@keyframes scale-out {
+ from {
+ transform: scale(1);
+ opacity: 1;
+ }
+ to {
+ transform: scale(0.95);
+ opacity: 0;
+ }
+}
+
+@keyframes bounce {
+ 0%, 100% {
+ transform: translateY(0);
+ animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+ }
+ 50% {
+ transform: translateY(-25%);
+ animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+ }
+}
+
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+@keyframes ping {
+ 0% {
+ transform: scale(1);
+ opacity: 1;
+ }
+ 75%, 100% {
+ transform: scale(2);
+ opacity: 0;
+ }
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: -1000px 0;
+ }
+ 100% {
+ background-position: 1000px 0;
+ }
+}
+
+@keyframes shake {
+ 0%, 100% {
+ transform: translateX(0);
+ }
+ 10%, 30%, 50%, 70%, 90% {
+ transform: translateX(-10px);
+ }
+ 20%, 40%, 60%, 80% {
+ transform: translateX(10px);
+ }
+}
+
+@keyframes swing {
+ 0%, 100% {
+ transform: rotate(0deg);
+ }
+ 20% {
+ transform: rotate(15deg);
+ }
+ 40% {
+ transform: rotate(-10deg);
+ }
+ 60% {
+ transform: rotate(5deg);
+ }
+ 80% {
+ transform: rotate(-5deg);
+ }
+}
+
+@keyframes rotate-in {
+ from {
+ transform: rotate(-180deg) scale(0.8);
+ opacity: 0;
+ }
+ to {
+ transform: rotate(0deg) scale(1);
+ opacity: 1;
+ }
+}
+
+@keyframes flip {
+ from {
+ transform: perspective(400px) rotateY(90deg);
+ opacity: 0;
+ }
+ to {
+ transform: perspective(400px) rotateY(0deg);
+ opacity: 1;
+ }
+}
+
+@keyframes glow {
+ 0%, 100% {
+ box-shadow: 0 0 5px rgba($accent-color, 0.2);
+ }
+ 50% {
+ box-shadow: 0 0 20px rgba($accent-color, 0.6);
+ }
+}
+
+@keyframes gradient-shift {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-20px);
+ }
+}
+
+@keyframes wave {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 10% {
+ transform: rotate(14deg);
+ }
+ 20% {
+ transform: rotate(-8deg);
+ }
+ 30% {
+ transform: rotate(14deg);
+ }
+ 40% {
+ transform: rotate(-4deg);
+ }
+ 50% {
+ transform: rotate(10deg);
+ }
+ 60% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(0deg);
+ }
+}
+
+@keyframes typing {
+ from {
+ width: 0;
+ }
+ to {
+ width: 100%;
+ }
+}
+
+@keyframes blink {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0;
+ }
+}
+
+.animate-fade-in {
+ animation: fade-in $transition-base forwards;
+}
+
+.animate-fade-out {
+ animation: fade-out $transition-base forwards;
+}
+
+.animate-slide-in-up {
+ animation: slide-in-up $transition-base $easing-ease-out;
+}
+
+.animate-slide-in-down {
+ animation: slide-in-down $transition-base $easing-ease-out;
+}
+
+.animate-slide-in-left {
+ animation: slide-in-left $transition-base $easing-ease-out;
+}
+
+.animate-slide-in-right {
+ animation: slide-in-right $transition-base $easing-ease-out;
+}
+
+.animate-scale-in {
+ animation: scale-in $transition-base $easing-ease-out;
+}
+
+.animate-scale-out {
+ animation: scale-out $transition-base $easing-ease-out;
+}
+
+.animate-bounce {
+ animation: bounce 1s infinite;
+}
+
+.animate-pulse {
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}
+
+.animate-ping {
+ animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+}
+
+.animate-spin {
+ animation: spin 1s linear infinite;
+}
+
+.animate-shimmer {
+ animation: shimmer 2s infinite;
+}
+
+.animate-shake {
+ animation: shake 0.5s;
+}
+
+.animate-swing {
+ animation: swing 1s ease-in-out;
+}
+
+.animate-rotate-in {
+ animation: rotate-in $transition-base $easing-ease-out;
+}
+
+.animate-flip {
+ animation: flip $transition-base $easing-ease-out;
+}
+
+.animate-glow {
+ animation: glow 2s ease-in-out infinite;
+}
+
+.animate-gradient-shift {
+ animation: gradient-shift 3s ease infinite;
+ background-size: 200% 200%;
+}
+
+.animate-float {
+ animation: float 3s ease-in-out infinite;
+}
+
+.animate-wave {
+ animation: wave 2s ease-in-out;
+}
+
+.animate-typing {
+ animation: typing 3.5s steps(40, end);
+ overflow: hidden;
+ white-space: nowrap;
+ border-right: 3px solid;
+ animation: typing 3.5s steps(40, end), blink 0.75s step-end infinite;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/src/styles/_utilities.scss b/src/styles/_utilities.scss
new file mode 100644
index 0000000..a5536fa
--- /dev/null
+++ b/src/styles/_utilities.scss
@@ -0,0 +1,333 @@
+@use 'sass:color';
+@use 'sass:map';
+
+$breakpoints: (
+ 'xs': 480px,
+ 'sm': 640px,
+ 'md': 768px,
+ 'lg': 1024px,
+ 'xl': 1280px,
+ '2xl': 1536px,
+);
+
+@function breakpoint($size) {
+ @return map.get($breakpoints, $size);
+}
+
+@mixin respond-to($breakpoint) {
+ @if map.has-key($breakpoints, $breakpoint) {
+ @media (min-width: map.get($breakpoints, $breakpoint)) {
+ @content;
+ }
+ } @else {
+ @warn "Unknown breakpoint: #{$breakpoint}";
+ }
+}
+
+@mixin respond-below($breakpoint) {
+ @if map.has-key($breakpoints, $breakpoint) {
+ @media (max-width: map.get($breakpoints, $breakpoint) - 1px) {
+ @content;
+ }
+ } @else {
+ @warn "Unknown breakpoint: #{$breakpoint}";
+ }
+}
+
+@mixin respond-between($min, $max) {
+ @if map.has-key($breakpoints, $min) and map.has-key($breakpoints, $max) {
+ @media (min-width: map.get($breakpoints, $min)) and (max-width: map.get($breakpoints, $max) - 1px) {
+ @content;
+ }
+ }
+}
+
+$spacing-scale: (
+ '0': 0,
+ '1': 0.25rem,
+ '2': 0.5rem,
+ '3': 0.75rem,
+ '4': 1rem,
+ '5': 1.25rem,
+ '6': 1.5rem,
+ '8': 2rem,
+ '10': 2.5rem,
+ '12': 3rem,
+ '16': 4rem,
+ '20': 5rem,
+ '24': 6rem,
+);
+
+@function spacing($size) {
+ @return map.get($spacing-scale, $size);
+}
+
+$color-palette: (
+ 'primary': (
+ 50: #eef2ff,
+ 100: #e0e7ff,
+ 200: #c7d2fe,
+ 300: #a5b4fc,
+ 400: #818cf8,
+ 500: #6366f1,
+ 600: #4f46e5,
+ 700: #4338ca,
+ 800: #3730a3,
+ 900: #312e81,
+ ),
+ 'secondary': (
+ 50: #faf5ff,
+ 100: #f3e8ff,
+ 200: #e9d5ff,
+ 300: #d8b4fe,
+ 400: #c084fc,
+ 500: #a855f7,
+ 600: #9333ea,
+ 700: #7e22ce,
+ 800: #6b21a8,
+ 900: #581c87,
+ ),
+ 'accent': (
+ 50: #ecfeff,
+ 100: #cffafe,
+ 200: #a5f3fc,
+ 300: #67e8f9,
+ 400: #22d3ee,
+ 500: #06b6d4,
+ 600: #0891b2,
+ 700: #0e7490,
+ 800: #155e75,
+ 900: #164e63,
+ ),
+ 'success': (
+ 50: #f0fdf4,
+ 100: #dcfce7,
+ 200: #bbf7d0,
+ 300: #86efac,
+ 400: #4ade80,
+ 500: #22c55e,
+ 600: #16a34a,
+ 700: #15803d,
+ 800: #166534,
+ 900: #14532d,
+ ),
+ 'error': (
+ 50: #fef2f2,
+ 100: #fee2e2,
+ 200: #fecaca,
+ 300: #fca5a5,
+ 400: #f87171,
+ 500: #ef4444,
+ 600: #dc2626,
+ 700: #b91c1c,
+ 800: #991b1b,
+ 900: #7f1d1d,
+ ),
+ 'warning': (
+ 50: #fffbeb,
+ 100: #fef3c7,
+ 200: #fde68a,
+ 300: #fcd34d,
+ 400: #fbbf24,
+ 500: #f59e0b,
+ 600: #d97706,
+ 700: #b45309,
+ 800: #92400e,
+ 900: #78350f,
+ ),
+ 'gray': (
+ 50: #f9fafb,
+ 100: #f3f4f6,
+ 200: #e5e7eb,
+ 300: #d1d5db,
+ 400: #9ca3af,
+ 500: #6b7280,
+ 600: #4b5563,
+ 700: #374151,
+ 800: #1f2937,
+ 900: #111827,
+ ),
+);
+
+@function get-color($palette, $shade: 500) {
+ @if map.has-key($color-palette, $palette) {
+ $palette-map: map.get($color-palette, $palette);
+ @return map.get($palette-map, $shade);
+ }
+ @warn "Unknown color palette: #{$palette}";
+ @return null;
+}
+
+@mixin theme-colors($theme-name) {
+ &[data-theme='#{$theme-name}'] {
+ @content;
+ }
+}
+
+@mixin transition($properties: all, $duration: 0.3s, $timing: ease) {
+ transition: $properties $duration $timing;
+}
+
+@mixin truncate($lines: 1) {
+ @if $lines == 1 {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ } @else {
+ display: -webkit-box;
+ -webkit-line-clamp: $lines;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ }
+}
+
+@mixin center($direction: 'both') {
+ display: flex;
+
+ @if $direction == 'both' {
+ justify-content: center;
+ align-items: center;
+ } @else if $direction == 'horizontal' {
+ justify-content: center;
+ } @else if $direction == 'vertical' {
+ align-items: center;
+ }
+}
+
+@mixin aspect-ratio($width, $height) {
+ aspect-ratio: #{$width} / #{$height};
+
+ @supports not (aspect-ratio: 1 / 1) {
+ &::before {
+ content: '';
+ padding-top: percentage($height / $width);
+ float: left;
+ }
+
+ &::after {
+ content: '';
+ display: block;
+ clear: both;
+ }
+ }
+}
+
+@mixin focus-ring($color: #06b6d4, $width: 3px, $offset: 2px) {
+ &:focus {
+ outline: none;
+ box-shadow: 0 0 0 $width rgba($color, 0.3);
+ }
+
+ &:focus-visible {
+ outline: $width solid rgba($color, 0.5);
+ outline-offset: $offset;
+ }
+}
+
+@mixin hide-scrollbar {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+}
+
+@mixin show-scrollbar($track-color: rgba(255, 255, 255, 0.1), $thumb-color: rgba(255, 255, 255, 0.3)) {
+ &::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: $track-color;
+ border-radius: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: $thumb-color;
+ border-radius: 4px;
+
+ &:hover {
+ background: color.adjust($thumb-color, $lightness: 10%);
+ }
+ }
+}
+
+@mixin visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+@mixin reset-button {
+ background: none;
+ border: none;
+ padding: 0;
+ margin: 0;
+ font: inherit;
+ color: inherit;
+ cursor: pointer;
+
+ &:focus {
+ outline: none;
+ }
+}
+
+@mixin reset-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+$font-sizes: (
+ 'xs': 0.75rem,
+ 'sm': 0.875rem,
+ 'base': 1rem,
+ 'lg': 1.125rem,
+ 'xl': 1.25rem,
+ '2xl': 1.5rem,
+ '3xl': 1.875rem,
+ '4xl': 2.25rem,
+ '5xl': 3rem,
+);
+
+@function font-size($size) {
+ @return map.get($font-sizes, $size);
+}
+
+$font-weights: (
+ 'thin': 100,
+ 'light': 300,
+ 'normal': 400,
+ 'medium': 500,
+ 'semibold': 600,
+ 'bold': 700,
+ 'extrabold': 800,
+ 'black': 900,
+);
+
+@function font-weight($weight) {
+ @return map.get($font-weights, $weight);
+}
+
+$z-index-layers: (
+ 'base': 0,
+ 'dropdown': 1000,
+ 'sticky': 1020,
+ 'fixed': 1030,
+ 'modal-backdrop': 1040,
+ 'modal': 1050,
+ 'popover': 1060,
+ 'tooltip': 1070,
+);
+
+@function z-index($layer) {
+ @return map.get($z-index-layers, $layer);
+}
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
new file mode 100644
index 0000000..9560a8c
--- /dev/null
+++ b/src/styles/_variables.scss
@@ -0,0 +1,72 @@
+$primary-color: #6366f1;
+$primary-light: #818cf8;
+$primary-dark: #4338ca;
+
+$secondary-color: #8b5cf6;
+$secondary-light: #a78bfa;
+$secondary-dark: #7c3aed;
+
+$accent-color: #06b6d4;
+$accent-light: #22d3ee;
+$accent-dark: #0891b2;
+
+$success-color: #10b981;
+$error-color: #ef4444;
+$warning-color: #f59e0b;
+$info-color: #3b82f6;
+
+$background-dark: #1a1d29;
+$background-card: #23273a;
+$background-elevated: #2d3348;
+$background-hover: rgba(255, 255, 255, 0.05);
+
+$text-primary: #e8eaed;
+$text-secondary: #a8abb5;
+$text-disabled: #6b6e7b;
+$text-inverse: #1a1d29;
+
+$border-color: rgba(255, 255, 255, 0.1);
+$border-color-strong: rgba(255, 255, 255, 0.2);
+$border-color-weak: rgba(255, 255, 255, 0.05);
+
+$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+$shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+$shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+
+$radius-sm: 0.25rem;
+$radius-md: 0.5rem;
+$radius-lg: 0.75rem;
+$radius-xl: 1rem;
+$radius-2xl: 1.5rem;
+$radius-full: 9999px;
+
+$spacing-xs: 0.25rem;
+$spacing-sm: 0.5rem;
+$spacing-md: 1rem;
+$spacing-lg: 1.5rem;
+$spacing-xl: 2rem;
+$spacing-2xl: 3rem;
+
+$font-family-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+$font-family-display: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+$font-family-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
+
+$transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
+$transition-base: 300ms cubic-bezier(0.4, 0, 0.2, 1);
+$transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1);
+
+$easing-linear: linear;
+$easing-ease: ease;
+$easing-ease-in: ease-in;
+$easing-ease-out: ease-out;
+$easing-ease-in-out: ease-in-out;
+$easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
+$easing-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
+
+$grid-columns: 12;
+$grid-gutter: 1rem;
+
+$container-max-width: 1280px;
+$container-padding: 1rem;
diff --git a/src/styles/main.scss b/src/styles/main.scss
new file mode 100644
index 0000000..f6d43c7
--- /dev/null
+++ b/src/styles/main.scss
@@ -0,0 +1,573 @@
+@use './variables' as *;
+@use './utilities' as *;
+@use './animations';
+@use './material-ui-custom';
+
+.mui-enhanced {
+ font-family: $font-family-sans;
+ color: $text-primary;
+
+ * {
+ box-sizing: border-box;
+ }
+
+ code, pre {
+ font-family: $font-family-mono;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ font-family: $font-family-display;
+ }
+}
+
+.custom-mui-container {
+ max-width: $container-max-width;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: $container-padding;
+ padding-right: $container-padding;
+
+ @include respond-to('lg') {
+ padding-left: calc($container-padding * 2);
+ padding-right: calc($container-padding * 2);
+ }
+}
+
+.custom-mui-grid {
+ display: grid;
+ gap: $grid-gutter;
+
+ &--cols-1 {
+ grid-template-columns: repeat(1, 1fr);
+ }
+
+ &--cols-2 {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ &--cols-3 {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ &--cols-4 {
+ grid-template-columns: repeat(4, 1fr);
+ }
+
+ &--cols-6 {
+ grid-template-columns: repeat(6, 1fr);
+ }
+
+ &--cols-12 {
+ grid-template-columns: repeat(12, 1fr);
+ }
+
+ &--responsive {
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ }
+}
+
+.custom-mui-flex {
+ display: flex;
+
+ &--row {
+ flex-direction: row;
+ }
+
+ &--col {
+ flex-direction: column;
+ }
+
+ &--wrap {
+ flex-wrap: wrap;
+ }
+
+ &--center {
+ justify-content: center;
+ align-items: center;
+ }
+
+ &--between {
+ justify-content: space-between;
+ }
+
+ &--around {
+ justify-content: space-around;
+ }
+
+ &--evenly {
+ justify-content: space-evenly;
+ }
+
+ &--start {
+ justify-content: flex-start;
+ align-items: flex-start;
+ }
+
+ &--end {
+ justify-content: flex-end;
+ align-items: flex-end;
+ }
+}
+
+.custom-mui-stack {
+ display: flex;
+ flex-direction: column;
+ gap: spacing('4');
+
+ &--horizontal {
+ flex-direction: row;
+ }
+
+ &--gap-1 {
+ gap: spacing('1');
+ }
+
+ &--gap-2 {
+ gap: spacing('2');
+ }
+
+ &--gap-3 {
+ gap: spacing('3');
+ }
+
+ &--gap-4 {
+ gap: spacing('4');
+ }
+
+ &--gap-6 {
+ gap: spacing('6');
+ }
+
+ &--gap-8 {
+ gap: spacing('8');
+ }
+}
+
+.custom-mui-surface {
+ background: $background-card;
+ border: 1px solid $border-color;
+ border-radius: $radius-lg;
+ padding: spacing('6');
+
+ &--elevated {
+ box-shadow: $shadow-lg;
+ background: $background-elevated;
+ }
+
+ &--interactive {
+ cursor: pointer;
+ transition: all $transition-base;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: $shadow-xl;
+ border-color: $border-color-strong;
+ }
+
+ &:active {
+ transform: translateY(0);
+ box-shadow: $shadow-md;
+ }
+ }
+}
+
+.custom-mui-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(4px);
+ z-index: z-index('modal-backdrop');
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ animation: fade-in $transition-fast;
+}
+
+.custom-mui-drawer {
+ position: fixed;
+ background: $background-card;
+ z-index: z-index('modal');
+ box-shadow: $shadow-2xl;
+ overflow-y: auto;
+
+ &--left {
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 320px;
+ animation: slide-in-left $transition-base;
+ }
+
+ &--right {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 320px;
+ animation: slide-in-right $transition-base;
+ }
+
+ &--top {
+ top: 0;
+ left: 0;
+ right: 0;
+ height: auto;
+ animation: slide-in-down $transition-base;
+ }
+
+ &--bottom {
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: auto;
+ max-height: 90vh;
+ animation: slide-in-up $transition-base;
+ }
+}
+
+.custom-mui-navbar {
+ position: sticky;
+ top: 0;
+ z-index: z-index('sticky');
+ background: rgba($background-card, 0.9);
+ backdrop-filter: blur(12px);
+ border-bottom: 1px solid $border-color;
+ padding: spacing('4') spacing('6');
+
+ @include respond-below('md') {
+ padding: spacing('3') spacing('4');
+ }
+}
+
+.custom-mui-sidebar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 280px;
+ background: $background-card;
+ border-right: 1px solid $border-color;
+ overflow-y: auto;
+ z-index: z-index('fixed');
+
+ @include respond-below('lg') {
+ transform: translateX(-100%);
+ transition: transform $transition-base;
+
+ &--open {
+ transform: translateX(0);
+ }
+ }
+}
+
+.custom-mui-footer {
+ background: $background-card;
+ border-top: 1px solid $border-color;
+ padding: spacing('8') spacing('6');
+ margin-top: auto;
+
+ @include respond-below('md') {
+ padding: spacing('6') spacing('4');
+ }
+}
+
+.custom-mui-breadcrumbs {
+ display: flex;
+ align-items: center;
+ gap: spacing('2');
+ flex-wrap: wrap;
+
+ &-item {
+ display: flex;
+ align-items: center;
+ color: $text-secondary;
+ font-size: font-size('sm');
+
+ &--active {
+ color: $text-primary;
+ font-weight: font-weight('medium');
+ }
+
+ &:not(:last-child)::after {
+ content: '/';
+ margin-left: spacing('2');
+ color: $text-disabled;
+ }
+ }
+
+ a {
+ color: inherit;
+ text-decoration: none;
+ transition: color $transition-fast;
+
+ &:hover {
+ color: $accent-color;
+ }
+ }
+}
+
+.custom-mui-stepper {
+ display: flex;
+ align-items: center;
+
+ &-step {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: spacing('2');
+ flex: 1;
+ position: relative;
+
+ &:not(:last-child)::after {
+ content: '';
+ position: absolute;
+ top: 16px;
+ left: 50%;
+ width: 100%;
+ height: 2px;
+ background: $border-color;
+ }
+
+ &--completed::after {
+ background: $success-color;
+ }
+
+ &--active::after {
+ background: $primary-color;
+ }
+ }
+
+ &-icon {
+ @include center;
+ width: 32px;
+ height: 32px;
+ border-radius: $radius-full;
+ background: $background-elevated;
+ border: 2px solid $border-color;
+ color: $text-secondary;
+ font-size: font-size('sm');
+ font-weight: font-weight('semibold');
+ z-index: 1;
+
+ .custom-mui-stepper-step--completed & {
+ background: $success-color;
+ border-color: $success-color;
+ color: white;
+ }
+
+ .custom-mui-stepper-step--active & {
+ background: $primary-color;
+ border-color: $primary-color;
+ color: white;
+ }
+ }
+
+ &-label {
+ font-size: font-size('sm');
+ color: $text-secondary;
+ text-align: center;
+
+ .custom-mui-stepper-step--active & {
+ color: $text-primary;
+ font-weight: font-weight('medium');
+ }
+ }
+}
+
+.custom-mui-timeline {
+ position: relative;
+ padding-left: spacing('6');
+
+ &::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 2px;
+ background: $border-color;
+ }
+
+ &-item {
+ position: relative;
+ padding-bottom: spacing('6');
+
+ &::before {
+ content: '';
+ position: absolute;
+ left: -29px;
+ top: 4px;
+ width: 12px;
+ height: 12px;
+ border-radius: $radius-full;
+ background: $primary-color;
+ border: 2px solid $background-dark;
+ }
+
+ &--success::before {
+ background: $success-color;
+ }
+
+ &--error::before {
+ background: $error-color;
+ }
+
+ &--warning::before {
+ background: $warning-color;
+ }
+ }
+}
+
+.custom-mui-table {
+ width: 100%;
+ border-collapse: collapse;
+
+ thead {
+ background: $background-elevated;
+ border-bottom: 2px solid $border-color-strong;
+ }
+
+ th {
+ padding: spacing('3') spacing('4');
+ text-align: left;
+ font-weight: font-weight('semibold');
+ font-size: font-size('sm');
+ color: $text-secondary;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ }
+
+ td {
+ padding: spacing('3') spacing('4');
+ border-bottom: 1px solid $border-color;
+ }
+
+ tbody tr {
+ transition: background $transition-fast;
+
+ &:hover {
+ background: $background-hover;
+ }
+ }
+
+ &--striped tbody tr:nth-child(even) {
+ background: rgba(255, 255, 255, 0.02);
+ }
+
+ &--bordered {
+ border: 1px solid $border-color;
+
+ td, th {
+ border: 1px solid $border-color;
+ }
+ }
+}
+
+.custom-mui-list {
+ @include reset-list;
+
+ &-item {
+ padding: spacing('3') spacing('4');
+ transition: background $transition-fast;
+ border-radius: $radius-md;
+ cursor: pointer;
+
+ &:hover {
+ background: $background-hover;
+ }
+
+ &--active {
+ background: rgba($primary-color, 0.1);
+ color: $primary-color;
+ font-weight: font-weight('medium');
+ }
+
+ &--disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ pointer-events: none;
+ }
+ }
+
+ &--bordered &-item {
+ border: 1px solid $border-color;
+ margin-bottom: spacing('2');
+ }
+
+ &--divided &-item {
+ border-bottom: 1px solid $border-color;
+ border-radius: 0;
+
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+}
+
+.custom-mui-code-block {
+ background: $background-elevated;
+ border: 1px solid $border-color;
+ border-radius: $radius-md;
+ padding: spacing('4');
+ overflow-x: auto;
+ font-family: $font-family-mono;
+ font-size: font-size('sm');
+
+ @include show-scrollbar;
+
+ pre {
+ margin: 0;
+ padding: 0;
+ }
+
+ code {
+ color: $text-primary;
+ font-family: inherit;
+ }
+}
+
+.custom-mui-tag {
+ display: inline-flex;
+ align-items: center;
+ gap: spacing('1');
+ padding: spacing('1') spacing('2');
+ border-radius: $radius-md;
+ font-size: font-size('xs');
+ font-weight: font-weight('medium');
+ background: rgba($primary-color, 0.15);
+ color: $primary-color;
+
+ &--sm {
+ padding: 2px spacing('1');
+ font-size: 10px;
+ }
+
+ &--lg {
+ padding: spacing('2') spacing('3');
+ font-size: font-size('sm');
+ }
+}
+
+.utility-text {
+ &-truncate {
+ @include truncate(1);
+ }
+
+ &-truncate-2 {
+ @include truncate(2);
+ }
+
+ &-truncate-3 {
+ @include truncate(3);
+ }
+}
+
+.utility-visually-hidden {
+ @include visually-hidden;
+}
+
+.utility-focus-ring {
+ @include focus-ring;
+}
diff --git a/src/styles/material-ui-custom.scss b/src/styles/material-ui-custom.scss
new file mode 100644
index 0000000..c1e277e
--- /dev/null
+++ b/src/styles/material-ui-custom.scss
@@ -0,0 +1,532 @@
+@use 'sass:color';
+@use 'sass:map';
+
+$primary-color: #6366f1;
+$secondary-color: #8b5cf6;
+$accent-color: #06b6d4;
+$background-dark: #1a1d29;
+$background-card: #23273a;
+$text-light: #e8eaed;
+
+@mixin elevation($level) {
+ @if $level == 1 {
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ } @else if $level == 2 {
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
+ } @else if $level == 3 {
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
+ } @else if $level == 4 {
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);
+ }
+}
+
+@mixin responsive-padding($base-padding) {
+ padding: $base-padding;
+
+ @media (max-width: 768px) {
+ padding: $base-padding * 0.75;
+ }
+
+ @media (max-width: 480px) {
+ padding: $base-padding * 0.5;
+ }
+}
+
+@mixin glassmorphism($blur: 10px, $opacity: 0.1) {
+ background: rgba(255, 255, 255, $opacity);
+ backdrop-filter: blur($blur);
+ -webkit-backdrop-filter: blur($blur);
+}
+
+@mixin gradient-background($start-color, $end-color, $angle: 135deg) {
+ background: linear-gradient($angle, $start-color, $end-color);
+}
+
+.mui-custom {
+ &-card {
+ @include elevation(2);
+ border-radius: 12px;
+ background-color: $background-card;
+ transition: all 0.3s ease;
+
+ &:hover {
+ @include elevation(3);
+ transform: translateY(-2px);
+ }
+
+ &--gradient {
+ @include gradient-background($primary-color, $secondary-color);
+ color: white;
+ }
+
+ &--glass {
+ @include glassmorphism(16px, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+ }
+
+ &-button {
+ @include responsive-padding(12px);
+ border-radius: 8px;
+ font-weight: 600;
+ text-transform: none;
+ transition: all 0.2s ease;
+
+ &--primary {
+ background: $primary-color;
+ color: white;
+
+ &:hover {
+ background: color.adjust($primary-color, $lightness: 10%);
+ box-shadow: 0 4px 12px rgba($primary-color, 0.4);
+ }
+
+ &:active {
+ transform: scale(0.98);
+ }
+ }
+
+ &--secondary {
+ background: $secondary-color;
+ color: white;
+
+ &:hover {
+ background: color.adjust($secondary-color, $lightness: 10%);
+ box-shadow: 0 4px 12px rgba($secondary-color, 0.4);
+ }
+ }
+
+ &--accent {
+ background: $accent-color;
+ color: $background-dark;
+
+ &:hover {
+ background: color.adjust($accent-color, $lightness: 10%);
+ box-shadow: 0 4px 12px rgba($accent-color, 0.4);
+ }
+ }
+
+ &--outline {
+ background: transparent;
+ border: 2px solid $primary-color;
+ color: $primary-color;
+
+ &:hover {
+ background: rgba($primary-color, 0.1);
+ }
+ }
+
+ &--ghost {
+ background: transparent;
+ color: $text-light;
+
+ &:hover {
+ background: rgba($text-light, 0.1);
+ }
+ }
+ }
+
+ &-input {
+ @include responsive-padding(12px);
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: $text-light;
+ transition: all 0.2s ease;
+
+ &:focus {
+ outline: none;
+ border-color: $accent-color;
+ box-shadow: 0 0 0 3px rgba($accent-color, 0.2);
+ }
+
+ &::placeholder {
+ color: rgba($text-light, 0.5);
+ }
+
+ &--error {
+ border-color: #ef4444;
+
+ &:focus {
+ box-shadow: 0 0 0 3px rgba(#ef4444, 0.2);
+ }
+ }
+
+ &--success {
+ border-color: #10b981;
+
+ &:focus {
+ box-shadow: 0 0 0 3px rgba(#10b981, 0.2);
+ }
+ }
+ }
+
+ &-chip {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 12px;
+ border-radius: 16px;
+ font-size: 12px;
+ font-weight: 500;
+
+ &--primary {
+ background: rgba($primary-color, 0.2);
+ color: $primary-color;
+ border: 1px solid rgba($primary-color, 0.3);
+ }
+
+ &--secondary {
+ background: rgba($secondary-color, 0.2);
+ color: $secondary-color;
+ border: 1px solid rgba($secondary-color, 0.3);
+ }
+
+ &--accent {
+ background: rgba($accent-color, 0.2);
+ color: $accent-color;
+ border: 1px solid rgba($accent-color, 0.3);
+ }
+
+ &--success {
+ background: rgba(#10b981, 0.2);
+ color: #10b981;
+ border: 1px solid rgba(#10b981, 0.3);
+ }
+
+ &--error {
+ background: rgba(#ef4444, 0.2);
+ color: #ef4444;
+ border: 1px solid rgba(#ef4444, 0.3);
+ }
+
+ &--warning {
+ background: rgba(#f59e0b, 0.2);
+ color: #f59e0b;
+ border: 1px solid rgba(#f59e0b, 0.3);
+ }
+ }
+
+ &-panel {
+ @include responsive-padding(24px);
+ @include elevation(1);
+ border-radius: 12px;
+ background: $background-card;
+
+ &--with-header {
+ padding: 0;
+
+ .panel-header {
+ @include responsive-padding(20px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ font-weight: 600;
+ font-size: 18px;
+ }
+
+ .panel-body {
+ @include responsive-padding(24px);
+ }
+
+ .panel-footer {
+ @include responsive-padding(20px);
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+ }
+ }
+ }
+
+ &-dialog {
+ .dialog-overlay {
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(4px);
+ }
+
+ .dialog-content {
+ @include elevation(4);
+ border-radius: 16px;
+ background: $background-card;
+ max-width: 600px;
+ animation: dialog-appear 0.3s ease;
+ }
+
+ @keyframes dialog-appear {
+ from {
+ opacity: 0;
+ transform: scale(0.95) translateY(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1) translateY(0);
+ }
+ }
+ }
+
+ &-tooltip {
+ @include elevation(2);
+ padding: 8px 12px;
+ border-radius: 6px;
+ background: $background-card;
+ color: $text-light;
+ font-size: 12px;
+ max-width: 200px;
+ animation: tooltip-appear 0.2s ease;
+
+ @keyframes tooltip-appear {
+ from {
+ opacity: 0;
+ transform: translateY(4px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+ }
+
+ &-avatar {
+ border-radius: 50%;
+ overflow: hidden;
+
+ &--small {
+ width: 32px;
+ height: 32px;
+ }
+
+ &--medium {
+ width: 48px;
+ height: 48px;
+ }
+
+ &--large {
+ width: 64px;
+ height: 64px;
+ }
+
+ &--with-ring {
+ box-shadow: 0 0 0 3px $accent-color;
+ }
+ }
+
+ &-badge {
+ position: relative;
+ display: inline-flex;
+
+ .badge-indicator {
+ position: absolute;
+ top: 0;
+ right: 0;
+ transform: translate(50%, -50%);
+ min-width: 20px;
+ height: 20px;
+ padding: 0 6px;
+ border-radius: 10px;
+ background: #ef4444;
+ color: white;
+ font-size: 11px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 2px solid $background-dark;
+ }
+
+ &--dot {
+ .badge-indicator {
+ min-width: 10px;
+ width: 10px;
+ height: 10px;
+ padding: 0;
+ border-radius: 50%;
+ }
+ }
+ }
+
+ &-skeleton {
+ background: linear-gradient(
+ 90deg,
+ rgba(255, 255, 255, 0.05) 25%,
+ rgba(255, 255, 255, 0.1) 50%,
+ rgba(255, 255, 255, 0.05) 75%
+ );
+ background-size: 200% 100%;
+ animation: skeleton-shimmer 1.5s infinite;
+ border-radius: 4px;
+
+ @keyframes skeleton-shimmer {
+ 0% {
+ background-position: 200% 0;
+ }
+ 100% {
+ background-position: -200% 0;
+ }
+ }
+
+ &--text {
+ height: 16px;
+ margin-bottom: 8px;
+ }
+
+ &--circle {
+ border-radius: 50%;
+ width: 48px;
+ height: 48px;
+ }
+
+ &--rect {
+ height: 120px;
+ }
+ }
+
+ &-progress {
+ width: 100%;
+ height: 4px;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 2px;
+ overflow: hidden;
+
+ .progress-bar {
+ height: 100%;
+ background: linear-gradient(90deg, $primary-color, $accent-color);
+ transition: width 0.3s ease;
+ }
+
+ &--indeterminate {
+ .progress-bar {
+ width: 40%;
+ animation: progress-indeterminate 1.5s infinite;
+ }
+ }
+
+ @keyframes progress-indeterminate {
+ 0% {
+ transform: translateX(-100%);
+ }
+ 100% {
+ transform: translateX(350%);
+ }
+ }
+ }
+
+ &-divider {
+ height: 1px;
+ background: rgba(255, 255, 255, 0.1);
+ margin: 16px 0;
+
+ &--vertical {
+ width: 1px;
+ height: auto;
+ margin: 0 16px;
+ }
+
+ &--with-text {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+
+ &::before,
+ &::after {
+ content: '';
+ flex: 1;
+ height: 1px;
+ background: rgba(255, 255, 255, 0.1);
+ }
+ }
+ }
+
+ &-accordion {
+ border-radius: 8px;
+ overflow: hidden;
+
+ &-item {
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+
+ &-trigger {
+ @include responsive-padding(16px);
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: transparent;
+ border: none;
+ color: $text-light;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background 0.2s ease;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.05);
+ }
+
+ .accordion-icon {
+ transition: transform 0.3s ease;
+ }
+
+ &[data-state='open'] .accordion-icon {
+ transform: rotate(180deg);
+ }
+ }
+
+ &-content {
+ @include responsive-padding(16px);
+ animation: accordion-slide-down 0.3s ease;
+
+ &[data-state='closed'] {
+ animation: accordion-slide-up 0.3s ease;
+ }
+ }
+
+ @keyframes accordion-slide-down {
+ from {
+ height: 0;
+ opacity: 0;
+ }
+ to {
+ height: var(--radix-accordion-content-height);
+ opacity: 1;
+ }
+ }
+
+ @keyframes accordion-slide-up {
+ from {
+ height: var(--radix-accordion-content-height);
+ opacity: 1;
+ }
+ to {
+ height: 0;
+ opacity: 0;
+ }
+ }
+ }
+}
+
+.custom-scrollbar {
+ &::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.2);
+ border-radius: 4px;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.3);
+ }
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ * {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}