From 3905226082ea98c81a18ae64db1a9f00dbbbed57 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 16 Jan 2026 01:48:07 +0000 Subject: [PATCH] Generated by Spark: Any non standard material UI CSS uses sass --- PRD.md | 7 + README.md | 4 + package-lock.json | 362 +++++++++++++++- package.json | 1 + src/App.tsx | 9 + src/components/DocumentationView.tsx | 408 ++++++++++++++++++ src/components/SassStylesShowcase.tsx | 320 ++++++++++++++ src/index.css | 1 + src/styles/_animations.scss | 364 ++++++++++++++++ src/styles/_utilities.scss | 333 +++++++++++++++ src/styles/_variables.scss | 72 ++++ src/styles/main.scss | 573 ++++++++++++++++++++++++++ src/styles/material-ui-custom.scss | 532 ++++++++++++++++++++++++ 13 files changed, 2984 insertions(+), 2 deletions(-) create mode 100644 src/components/SassStylesShowcase.tsx create mode 100644 src/styles/_animations.scss create mode 100644 src/styles/_utilities.scss create mode 100644 src/styles/_variables.scss create mode 100644 src/styles/main.scss create mode 100644 src/styles/material-ui-custom.scss 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

+ +
+
+
+ ) +}`} +
+
+ + + +
+

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 + + +
+
+ + + +
+ +
+ + +
+
+ +
+
+{`
+
+`}
+                
+
+
+
+
+ + + + + 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

+
+
+ +
+
+{`
+
Item 1
+
Item 2
+
+ +
+
Grid Item
+
`} +
+
+
+
+
+ + + + + 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; + } +}