Compare commits

...

44 Commits

Author SHA1 Message Date
seccodesmith 2cfdd61cc2 Update README.md 2025-05-27 15:18:53 +00:00
seccodesmith d9bdf12328 Merge pull request 'final-touches' (#10) from final-touches into main
Reviewed-on: #10
2025-05-27 14:38:29 +00:00
seccodesmith d00712c287 Merge branch 'main' into final-touches 2025-05-27 14:38:24 +00:00
seccodesmith 59dd998a5d feat: update project image paths and enhance project details in ProjectProps.json 2025-05-27 16:37:23 +02:00
seccodesmith 8d440c1993 feat: Add JSON data files for About, Blog Posts, Contact, Projects, Skills, and Social Links
- Created AboutProps.json with core values, technical arsenal, professional journey, and testimonials.
- Added BlogPosts.json for blog post data including title, content, and metadata.
- Introduced Contact.json for contact information and FAQs.
- Created ProjectProps.json as a template for project details.
- Added SkillCardProps.json for skill categories and skills.
- Introduced SocialLink.json for social media links.
- Updated components to fetch data from newly created JSON files instead of hardcoded values.
- Adjusted TypeScript configuration to support JSON module imports.
- Enhanced Vite configuration for public directory and build targets.
2025-05-27 16:14:15 +02:00
seccodesmith b855cc9127 feat: refactor data structures and interfaces for better organization 2025-05-27 14:10:27 +02:00
seccodesmith fe463c4fcb Merge pull request 'fix-contact' (#9) from fix-contact into main
Reviewed-on: #9
2025-05-27 11:53:44 +00:00
seccodesmith 29218b0e38 feat: add Accordion styling and integrate into Contact page 2025-05-27 13:52:14 +02:00
seccodesmith cd684dbaf7 feat: implement styling for Contact page and form components 2025-05-27 13:41:26 +02:00
seccodesmith 6b9522153f feat: update Contact page to use dynamic email and social links 2025-05-27 12:30:22 +02:00
seccodesmith 805bd07eb4 feat: add contact data and styling for the Contact page 2025-05-26 23:45:39 +02:00
seccodesmith e08c2d4013 Fix list in project modal 2025-05-26 22:42:05 +02:00
seccodesmith 56e8c2c252 Merge pull request 'Fix-projects' (#8) from Fix-projects into main
Reviewed-on: #8
2025-05-26 20:40:50 +00:00
seccodesmith b1816ec604 feat: update ProjectModal and projectsData for enhanced project details and technology display 2025-05-26 22:34:44 +02:00
seccodesmith e8a3611dfb feat: refactor BlogCard, ProjectCard, and ProjectModal components to use new type definitions and improve structure 2025-05-26 18:09:09 +02:00
seccodesmith 8e2030b0ab feat: enhance ProjectModal and Projects components by removing unused onClose prop and improving modal styling 2025-05-26 00:06:30 +02:00
seccodesmith c539be3c87 feat: update BlogCard and Project components with improved styling and structure 2025-05-24 22:31:34 +02:00
seccodesmith bce2276dfa feat: refactor ProjectCard and Projects components for improved category handling and styling 2025-05-24 19:31:43 +02:00
seccodesmith 7b2566d985 feat: enhance ProjectCard and Projects components with new styles and improved structure
style: update opacity in Home styles for better visibility
refactor: remove unnecessary comments in Projects component for cleaner code
2025-05-23 22:19:04 +02:00
seccodesmith e526fac14a Merge pull request 'Fixing-about' (#7) from Fixing-about into main
Reviewed-on: #7
2025-05-23 18:18:48 +00:00
seccodesmith 5bf0ffbad2 feat: update About page with new professional journey entry and testimonials section enhancements 2025-05-23 20:18:08 +02:00
seccodesmith 9ae26fbd9f style: enhance About page styles with responsive design and improved section titles 2025-05-23 19:29:51 +02:00
seccodesmith 6d32ddbfd0 refactor: standardize class naming conventions in About module styles to camelCase 2025-05-23 18:51:46 +02:00
seccodesmith 6ea40432f7 feat: add About section with core values, technical skills, and testimonials 2025-05-23 18:36:37 +02:00
seccodesmith 35c0a17f8a Merge pull request 'Fixing-blog' (#6) from Fixing-blog into main
Reviewed-on: #6
2025-05-23 10:01:37 +00:00
seccodesmith 9b2c295dd8 Merge branch 'main' into Fixing-blog 2025-05-23 10:01:31 +00:00
seccodesmith 45ebea5dab feat: add pagination functionality to Blog component and add function to automatic hide / show next previoce button 2025-05-23 12:00:51 +02:00
seccodesmith 7cab6ddea5 feat: implement dynamic Table of Contents in BlogPost component 2025-05-23 11:23:13 +02:00
seccodesmith 574e87bf99 feat: enhance BlogPost component with Table of Contents functionality and content formatting improvements 2025-05-23 10:42:36 +02:00
seccodesmith 4cc133fffa feat: enhance BlogPost component with dedent function, update styles, and improve content formatting 2025-05-23 09:49:37 +02:00
seccodesmith 142772a08a feat: add BlogPost styles using CSS modules, refactor BlogPost component for improved styling and structure 2025-05-22 23:01:16 +02:00
seccodesmith 3da8c4bddf Merge pull request 'feat: enhance responsive styles for title, subtitle, and buttons in Home component' (#5) from Fixing-home-modal into main
Reviewed-on: #5
2025-05-22 13:55:18 +00:00
seccodesmith 7d5a678008 feat: enhance responsive styles for title, subtitle, and buttons in Home component 2025-05-22 15:54:04 +02:00
seccodesmith b6894fac9b feat: refactor PageHeader component to use CSS modules and remove unused styles from Blog module 2025-05-22 15:44:26 +02:00
seccodesmith 77ba446d4d feat: add Blog and BlogCard styles, refactor components to use CSS modules, and remove unused CSS files 2025-05-22 15:05:24 +02:00
seccodesmith 762fc64806 Merge pull request 'Fixing-main' (#4) from Fixing-main into main
Reviewed-on: #4
2025-05-21 21:50:35 +00:00
seccodesmith 3b86302a6e feat: implement dynamic social links in Footer and add random code line data for Home component 2025-05-21 23:49:34 +02:00
seccodesmith 29b9dd93e2 feat: remove unused SVG files, add skill card data, and refactor Home component imports 2025-05-21 12:55:06 +02:00
seccodesmith d3f7368ba8 feat: update favicon and background images, refactor styles, and enhance skill card data 2025-05-21 12:13:48 +02:00
seccodesmith 7e91215af1 Merge pull request 'Trying-scss' (#3) from Trying-scss into main
Reviewed-on: #3
2025-05-20 19:07:28 +00:00
seccodesmith 03a9f61a2a feat: refactor Home and SkillCard components, add skillCard data for dynamic rendering 2025-05-20 20:51:24 +02:00
seccodesmith fdb79c70ad feat: update Font Awesome and DevIcon versions, refactor styles in Home and SkillCard components 2025-05-20 20:01:33 +02:00
seccodesmith fb84bf87de feat: refactor styles and update imports for Home and Blog components 2025-05-20 14:43:49 +02:00
seccodesmith 848318a23e feat: update dependencies and add new styles
- Added node and sass to dependencies in package.json
- Added @types/node to devDependencies
- Removed unused CSS files and replaced them with SCSS files
- Updated import paths in App.tsx and Blog.tsx to use new aliasing
- Configured TypeScript paths for easier imports
- Updated Vite config to include path aliases
- Created new SCSS files for About, Blog, Error, and Main styles
- Added a variables file for color and effect variables
- Updated index.scss for global styles and responsive adjustments
2025-05-18 14:29:54 +02:00
74 changed files with 5733 additions and 3090 deletions
+150 -1
View File
@@ -1,2 +1,151 @@
# seccodesmith-frontend # SecCodeSmith Frontend Portfolio
![Image](./public/images/SecCodeSmithFrontProj/Main.png)
A modern portfolio website with a unique fantasy blacksmith and dragon theme, showcasing my development skills and projects.
## 🐉 About
This portfolio represents my journey as a developer, designed with the aesthetic of a fantasy blacksmith working alongside dragons. The site combines modern web technologies with creative storytelling to create an engaging user experience.
**Live Demo**: [https://seccodesmith.pl](https://seccodesmith.pl)
## 🛠️ Built With
- **[Vite](https://vitejs.dev/)** - Next Generation Frontend Tooling
- **[React](https://reactjs.org/)** - JavaScript library for building user interfaces
- **[TypeScript](https://www.typescriptlang.org/)** - JavaScript with syntax for types
- **[SCSS](https://sass-lang.com/)** - CSS preprocessor for better styling
## 📋 Prerequisites
Before you begin, ensure you have the following installed:
- **Node.js** (v16.0.0 or higher)
- **npm** (v8.0.0 or higher) or **yarn** (v1.22.0 or higher)
You can check your versions by running:
```bash
node --version
npm --version
```
## 🚀 Getting Started
### Installation
1. Clone the repository:
```bash
git clone https://git.seccodesmith.pl/seccodesmith/seccodesmith-frontend.git
cd seccodesmith-frontend
```
2. Install dependencies:
```bash
npm install
# or
yarn install
```
### Running the Project
#### Development Mode
To run the project in development mode with hot-reload:
```bash
npm run dev
# or
yarn dev
```
The application will be available at `http://localhost:5173` (default Vite port).
#### Production Build
To build the project for production:
```bash
npm run build
# or
yarn build
```
#### Preview Production Build
To preview the production build locally:
```bash
npm run preview
# or
yarn preview
```
## 📁 Project Structure
```
seccodesmith-frontend/
├── src/
│ ├── assets/ # Images, fonts, and other static assets
│ ├── components/ # React components
│ ├── styles/ # SCSS files
│ ├── pages/ # Page components
│ ├── utils/ # Utility functions
│ ├── App.tsx # Main App component
│ └── main.tsx # Application entry point
├── public/ # Public static files
├── index.html # HTML template
├── vite.config.ts # Vite configuration
├── tsconfig.json # TypeScript configuration
├── package.json # Project dependencies and scripts
└── README.md # Project documentation
```
## ✨ Features
- Responsive design that works on all devices
- Interactive animations and transitions
- Dark/Light theme toggle
- Project showcase with detailed descriptions
- Contact form integration
- Performance optimized with Vite
## 🧪 Available Scripts
- `npm run dev` - Start development server
- `npm run build` - Build for production
- `npm run preview` - Preview production build
- `npm run lint` - Run ESLint
- `npm run type-check` - Run TypeScript compiler check
## 🎨 Customization
To customize the portfolio for your own use:
1. Update personal information in `src/data/personal.ts`
2. Replace projects in `src/data/projects.ts`
3. Modify color schemes in `src/styles/variables.scss`
4. Update images in `src/assets/`
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 🤝 Contributing
While this is a personal portfolio project, suggestions and feedback are welcome! Feel free to:
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
## 📬 Contact
**SecCodeSmith** - [contact@seccodesmith.pl](mailto:contact@seccodesmith.pl)
Project Link: [https://git.seccodesmith.pl/seccodesmith/seccodesmith-frontend](https://git.seccodesmith.pl/seccodesmith/seccodesmith-frontend)
## 🙏 Acknowledgments
- Inspiration from fantasy blacksmith and dragon themes
- [React Icons](https://react-icons.github.io/react-icons/) for icon library
- [Vite Documentation](https://vitejs.dev/guide/) for build tool guidance
---
⚔️ Forged with passion by SecCodeSmith 🐲
+5 -1
View File
@@ -7,12 +7,16 @@
<!-- Font Awesome --> <!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- DevIcon --> <!-- DevIcon -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.15.1/devicon.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.16.0/devicon.min.css">
<!-- Google Fonts --> <!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Fira+Code:wght@400;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Fira+Code:wght@400;600&display=swap" rel="stylesheet">
<!-- Bootstrap CSS --> <!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- highlight CSS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<title>SecCodeSmith - Code Forged in Digital Fire</title> <title>SecCodeSmith - Code Forged in Digital Fire</title>
<link rel="favicon" href="public/favicon.ico" type="image/x-icon">
<meta name="description" content="SecCodeSmith - Master craftsman of code specializing in embedded systems, machine learning, and web development. Forging digital solutions with security and precision."> <meta name="description" content="SecCodeSmith - Master craftsman of code specializing in embedded systems, machine learning, and web development. Forging digital solutions with security and precision.">
</head> </head>
<body> <body>
+866 -1011
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -11,14 +11,17 @@
}, },
"dependencies": { "dependencies": {
"@testing-library/react": "^16.3.0", "@testing-library/react": "^16.3.0",
"node": "^20.19.2",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-router-dom": "^7.6.0", "react-router-dom": "^7.6.0",
"sass": "^1.89.0",
"vitest": "^3.1.3" "vitest": "^3.1.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.25.0", "@eslint/js": "^9.25.0",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@types/node": "^22.15.18",
"@types/react": "^19.1.2", "@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2", "@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.4.1", "@vitejs/plugin-react": "^4.4.1",
@@ -26,6 +29,7 @@
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19", "eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0", "globals": "^16.0.0",
"path": "^0.12.7",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"typescript-eslint": "^8.30.1", "typescript-eslint": "^8.30.1",
"vite": "^6.3.5" "vite": "^6.3.5"
+87
View File
@@ -0,0 +1,87 @@
{
"coreValues": [
{
"title": "Security by Design",
"icon": "fas fa-shield-alt",
"description": "Security is not an afterthought but the very foundation upon which robust systems are built. Every line of code is crafted with defensive principles in mind."
},
{
"icon": "fas fa-tachometer-alt",
"title": "Performance Optimization",
"description": "Like a finely-honed blade, code should be both effective and efficient. I meticulously optimize every component for maximum speed and minimal resource consumption."
},
{
"icon": "fas fa-gem",
"title": "Elegant Simplicity",
"description": "True mastery is revealed not in complexity but in elegant simplicity. I strive to create solutions that are accessible, maintainable, and beautifully crafted."
},
{
"icon": "fas fa-sync-alt",
"title": "Continuous Evolution",
"description": "The digital landscape changes with the seasons. I embrace continuous learning and adaptation, integrating new techniques and technologies to forge ever-better solutions."
}
],
"technicalArsenal": [
{
"icon": "fas fa-microchip",
"title": "Embedded Systems",
"skills": [
"STM32 microcontroller programming (I2C, SPI, UART)",
"ESP32 wireless solutions",
"Firmware development with C/C++",
"Raspberry Pi and Pico-SDK integration",
"RTOS implementation"
]
},
{
"icon": "fas fa-brain",
"title": "AI & Data Analysis",
"skills": [
"Machine learning with PyTorch",
"Data manipulation with Pandas",
"Predictive modeling via Scikit-learn",
"Computer vision with Scikit-image",
"Edge AI deployment"
]
},
{
"icon": "fas fa-window-maximize",
"title": "Web Development",
"skills": [
"Web Development",
"Backend development with ASP.NET",
"Database management with Entity Framework",
"Frontend frameworks: Blazor, React",
"Responsive design with Bootstrap",
"Python-based web solutions with Django"
]
},
{
"icon": "fas fa-server",
"title": "DevOps & Infrastructure",
"skills": [
"Containerization with Docker",
"Multi-container orchestration via Docker-compose",
"Linux server administration",
"CI/CD pipeline implementation",
"Build system configuration with CMake"
]
}
],
"professionalJourney": [
{
"title": "Student at Rzeszów University of Technology",
"description": "Specialization: Computer Science",
"duration": "2021.10 - 2025.6"
},
{
"title": "intern software engineer at Opeteam S.A.",
"description": "- Performing programming tasks in R&D projects within the product development department,\n - Database administration, \n - Defining unit tests and manual testing of solutions, \n - Selecting technologies for basic problems, - Documenting the source code (technical documentation)., \n",
"duration": "2022.8 - 2023.4"
}
],
"testimonials": [
]
}
+19
View File
@@ -0,0 +1,19 @@
[
{
"id": "1",
"slug": "StartOFJurney",
"title": "Something ends, something begins.",
"excerpt": "",
"image": "images/Begining.jpg",
"category": "Story",
"date": "27-05-2025",
"author": "SecCodeSmith (Jakub)",
"commentCount": 0,
"readTime": "10m",
"featured": true,
"tags": [
"journey"
],
"content": "# I created this blog as the start of building my professional image, \n a place to document my projects and share my experiences. It's also a personal challenge for me. \n I am about to finish my studies and step into a new chapter of my professional life. \n On this blog, I will describe my projects and the lessons I've learned along the way. \n My first project will be creating my own website, featuring a blog, information about me, and descriptions of my work. \n See you soon!"
}
]
+18
View File
@@ -0,0 +1,18 @@
{
"email": "forge@seccodesmith.pl",
"businessEmail": "business@seccodesmith.pl",
"socialLinks": [
{ "platform": "LinkedIn", "url": "https://www.linkedin.com/in/jakub-berechowski", "icon": "fab fa-linkedin" },
{ "platform": "GitHub", "url": "https://github.com/seccodesmith", "icon": "fab fa-github" },
{ "platform": "Facebook", "url": "https://www.facebook.com/people/Seccodesmith/61575058536717/", "icon": "fab fa-facebook" }
],
"questionsAndAnswers": [
{ "question": "What is your typical project timeline?", "answer": "Project timelines vary based on complexity and scope. Small projects typically take 2-4 weeks, medium-sized projects 1-2 months, and larger enterprise solutions 3-6 months. I provide detailed timeline estimates during the initial consultation, and maintain transparent communication about progress throughout the development cycle." },
{ "question": "How does your collaboration process work?", "answer": "My collaboration process consists of five phases: initial consultation, requirements gathering, design and planning, implementation, and testing/deployment. I use agile methodologies with regular check-ins to ensure your vision is being realized. Communication is maintained through your preferred channels (email, Slack, etc.), and you\"ll have access to development milestones through shared repositories and project management tools." },
{ "question": "What are your payment terms and methods?", "answer": "I typically work with a milestone-based payment structure: 30% upfront to begin work, 30% at the midpoint after key deliverables are approved, and 40% upon project completion. For longer projects, we can establish additional milestones. I accept bank transfers, PayPal, and cryptocurrency payments. All payment terms are clearly outlined in the project contract before work begins."},
{ "question": "Who owns the code and deliverables?", "answer": "Upon final payment, you receive full ownership rights to all custom code and deliverables created specifically for your project. I maintain no proprietary claim to custom solutions developed for clients. For projects that incorporate open-source components, those elements remain under their original licenses, and I provide clear documentation of all third-party code used in your project." },
{ "question": "Do you offer support and maintenance?", "answer": "Yes, I offer a 30-day warranty period for all projects, during which bug fixes and minor adjustments are covered at no additional cost. For ongoing support, I provide maintenance packages that include regular updates, security patches, and priority response times. These can be tailored to your specific needs and budget, whether you require occasional support or dedicated monthly maintenance."}
],
"MapIframeUrl": "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d82077.2903513271!2d21.9433946!3d50.0411861!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x473cfae3cc14d449%3A0xd2240d31b33eb2ed!2zUnplc3rDs3c!5e0!3m2!1sen!2spl!4v1651813309336!5m2!1sen!2spl",
"phone": ""
}
+43
View File
@@ -0,0 +1,43 @@
[
{
"id": "SecCodeSmithFrontend",
"title": "SecCodeSmith Portfolio Page - Frontend",
"description": "A modern, responsive portfolio page showcasing my projects, skills, and blog posts. Built with React and styled-components for a sleek user interface.",
"image": "/images/SecCodeSmithFrontProj/Main.png",
"category": ["Web"],
"featured": true,
"technologies": [
{ "name": "React", "icon": "devicon-react-original tech-icon" },
{ "name": "TypeScript", "icon": "devicon-typescript-plain tech-icon" },
{ "name": "SCSS", "icon": "devicon-sass-original tech-icon" },
{ "name": "Vite", "icon": "devicon-vitejs-plain tech-icon" }
],
"links": {
"github": "https://github.com/SecCodeSmith/",
"demo": "https://seccodesmith.pl"
},
"projectDetails": {
"descriptions": [
"This portfolio page is designed to highlight my skills and projects in a visually appealing way. It features a clean layout, smooth animations, and responsive design for optimal viewing on all devices.",
"The project utilizes React for building the user interface, TypeScript for type safety, and SCSS for styling. The use of Vite ensures fast development and build times."
],
"startDate": "12-05-2025",
"endDate": "",
"dateFormatted": "DD MMMM YYYY",
"role": "Frontend Developer",
"status": "In Progress",
"keyFeatures": [
"Responsive design with mobile-first approach",
"Dynamic project showcase with filtering options",
"Interactive blog section with latest posts"
],
"gallery": ["/images/SecCodeSmithFrontProj/1.png", "/images/SecCodeSmithFrontProj/2.png", "/images/SecCodeSmithFrontProj/3.png", "/images/SecCodeSmithFrontProj/4.png" ],
"fullTechStack": [
{ "name": "React", "icons": "devicon-react-original" },
{ "name": "TypeScript", "icons": "devicon-typescript-plain" },
{ "name": "SCSS", "icons": "devicon-sass-original" },
{ "name": "Vite", "icons": "devicon-vitejs-plain" }
]
}
}
]
+58
View File
@@ -0,0 +1,58 @@
[
{
"categoryIcon": "fa-code",
"categoryTitle": "Languages",
"skills": [
{ "name": "C/C++", "icon": "devicon-cplusplus-plain" },
{ "name": "Python", "icon": "devicon-python-plain" },
{ "name": ".NET", "icon": "devicon-dotnetcore-plain" },
{ "name": "C#", "icon": "devicon-csharp-plain" }
]
},
{
"categoryIcon": "fa-microchip",
"categoryTitle": "Embedded Systems",
"skills": [
{ "name": "STM32 (I2C, SPI, UART)", "icon": "fas fa-microchip" },
{ "name": "ESP32", "icon": "fas fa-wifi" },
{ "name": "Pico-SDK", "icon": "fas fa-memory" }
]
},
{
"categoryIcon": "fa-brain",
"categoryTitle": "Data & ML",
"skills": [
{ "name": "Pandas", "icon": "devicon-pandas-plain" },
{ "name": "Scikit-learn", "icon": "devicon-scikitlearn-plain" },
{ "name": "Scikit-image", "icon": "fas fa-image" },
{ "name": "PyTorch", "icon": "devicon-pytorch-plain" },
{ "name": "OpenCV", "icon": "devicon-opencv-plain" }
]
},
{
"categoryIcon": "fas fa-window-maximize",
"categoryTitle": "Web Development",
"skills": [
{ "name": "ASP.NET Core", "icon": "devicon-dot-net-plain" },
{ "name": "Entity Framework", "icon": "fas fa-database" },
{ "name": "Blazor", "icon": "devicon-dotnetcore-plain" },
{ "name": "React", "icon": "devicon-react-original" },
{ "name": "Django", "icon": "devicon-django-plain" },
{ "name": "Bootstrap", "icon": "devicon-bootstrap-plain" }
]
},
{
"categoryIcon": "fas fa-cloud",
"categoryTitle": "DevOps & Systems",
"skills": [
{ "name": "Docker", "icon": "devicon-docker-plain" },
{ "name": "Docker-compose", "icon": "fas fa-layer-group" },
{ "name": "Linux CLI", "icon": "devicon-bash-plain" },
{ "name": "Git", "icon": "devicon-git-plain" },
{ "name": "CI/CD", "icon": "fas fa-cogs" },
{ "name": "Cmake", "icon": "devicon-cmake-plain" },
{ "name": "Linux", "icon": "devicon-linux-plain" },
{ "name": "Jenkins", "icon": "devicon-jenkins-plain" }
]
}
]
+22
View File
@@ -0,0 +1,22 @@
[
{
"icon": "fab fa-github",
"url": ""
},
{
"icon": "fab fa-linkedin",
"url": ""
},
{
"icon": "fab fa-twitter",
"url": ""
},
{
"icon": "fab fa-discord",
"url": ""
},
{
"icon": "fas fa-envelope",
"url": ""
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

-42
View File
@@ -1,42 +0,0 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
+1 -1
View File
@@ -7,7 +7,7 @@ import { BlogPost } from './pages/BlogPost'
import { Projects } from './pages/Projects' import { Projects } from './pages/Projects'
import { Contact } from './pages/Contact' import { Contact } from './pages/Contact'
import { NotFound } from './pages/NotFound' import { NotFound } from './pages/NotFound'
import './components/styles.css' import '@components/styles.css'
function App() { function App() {
return ( return (

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

+618
View File
@@ -0,0 +1,618 @@
@use './variables' as *;
/* Section styling */
.sectionTitle {
font-size: 2.25rem;
margin-bottom: 1.5rem;
position: relative;
padding-bottom: 1rem;
display: inline-block;
}
.sectionTitle::after {
content: "";
position: absolute;
width: 50px;
height: 3px;
background: $accent-fire;
bottom: 0;
left: 0;
box-shadow: $glow-effect;
}
.profileImage {
width: 100%;
aspect-ratio: 1/1;
border-radius: 8px;
overflow: hidden;
border: 3px solid rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
position: relative;
}
.profileImage::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, rgba(255, 100, 0, 0.2), transparent);
z-index: 1;
}
.profileImage img {
width: 100%;
height: 100%;
object-fit: cover;
filter: contrast(1.1) brightness(0.9);
}
.introText {
font-size: 1.1rem;
line-height: 1.8;
color: $text-ash;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
}
.introText p {
margin-bottom: 1.25rem;
}
.introText strong {
color: $text-light;
}
.introText .accent {
color: $accent-fire;
}
.connectLink {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 20px;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
text-decoration: none;
color: $text-light;
transition: all 0.3s ease;
border: 1px solid $card-border;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
}
.connectLink:hover {
background-color: rgba(255, 100, 0, 0.1);
transform: translateY(-3px);
border-color: $accent-fire;
color: $text-light;
}
.connectIcon {
color: $accent-fire;
}
/* Value cards */
.valueCard {
background-color: $card-bg;
border-radius: 8px;
padding: 1.875rem;
border: 1px solid $card-border;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
text-align: center;
height: 100%;
}
.valueCard:hover {
transform: translateY(-5px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.valueIcon {
font-size: 2.5rem;
color: $accent-fire;
margin-bottom: 1.25rem;
}
.valueTitle {
font-size: 1.3rem;
margin-bottom: 1rem;
position: relative;
display: inline-block;
color: $text-light;
}
.valueTitle::after {
content: "";
position: absolute;
width: 40px;
height: 2px;
background: $accent-fire;
bottom: -5px;
left: 50%;
transform: translateX(-50%);
}
.valueText {
color: $text-ash;
line-height: 1.6;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* Skills card */
.skillCard {
background-color: $card-bg;
border-radius: 8px;
padding: 1.875rem;
border: 1px solid $card-border;
transition: all 0.3s ease;
position: relative;
height: 100%;
}
.skillCard:hover {
transform: translateY(-10px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.skillCard::before {
content: "";
position: absolute;
width: 100%;
height: 5px;
top: 0;
left: 0;
background: linear-gradient(to right, $accent-dragon, $accent-fire);
border-radius: 8px 8px 0 0;
}
.skillHeader {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 1.25rem;
padding-bottom: 0.625rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.skillIcon {
font-size: 2rem;
color: $accent-fire;
}
.skillTitle {
font-size: 1.3rem;
margin-bottom: 0;
color: $text-light;
}
.skillCheck {
color: $accent-fire;
margin-right: 0.625rem;
}
/* Timeline */
.timeline {
position: relative;
padding: 0;
margin-top: 3rem;
list-style: none;
}
.timeline:before {
content: "";
position: absolute;
width: 3px;
height: 100%;
background: linear-gradient(to bottom, $accent-fire, $accent-dragon, rgba(255, 100, 0, 0.2));
left: 50%;
transform: translateX(-50%);
z-index: 1;
}
.timelineItem {
position: relative;
margin-bottom: 3rem;
width: 100%;
}
.timelineItem:nth-child(odd) .timelineContent {
margin-left: auto;
padding-left: 3rem;
border-radius: 8px;
}
.timelineItem:nth-child(even) .timelineContent {
margin-right: auto;
padding-right: 3rem;
border-radius: 8px;
}
.timelineDot {
width: 16px;
height: 16px;
background-color: $accent-fire;
border-radius: 50%;
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
box-shadow: $glow-effect;
z-index: 2;
}
.timelineDate {
display: inline-block;
font-size: 0.9rem;
background-color: rgba(255, 100, 0, 0.1);
color: $accent-fire;
padding: 5px 10px;
border-radius: 4px;
margin-bottom: 1rem;
font-weight: 600;
letter-spacing: 1px;
}
.timelineTitle {
font-size: 1.3rem;
margin-bottom: 0.625rem;
color: $text-light;
}
.timelineText {
color: $text-ash;
line-height: 1.6;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.timelineContent {
background-color: $card-bg;
border-radius: 8px;
padding: 1.5rem;
border: 1px solid $card-border;
position: relative;
transition: all 0.3s ease;
width: 45%;
}
.timelineContent:hover {
transform: translateY(-5px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
/* Testimonials */
.testimonial {
background-color: $card-bg;
border-radius: 8px;
padding: 1.875rem;
margin-bottom: 1.875rem;
border: 1px solid $card-border;
transition: all 0.3s ease;
position: relative;
}
.testimonial:hover {
transform: translateY(-5px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.testimonialContent {
padding-left: 1.25rem;
border-left: 3px solid $accent-fire;
font-style: italic;
color: $text-ash;
margin-bottom: 1.25rem;
line-height: 1.7;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.authorAvatar {
width: 60px;
height: 60px;
border-radius: 50%;
overflow: hidden;
}
.authorAvatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.authorName {
font-weight: 600;
font-size: 1.1rem;
color: $text-light;
margin-bottom: 0;
}
.authorTitle {
color: $text-ash;
font-size: 0.9rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* Blog post specific styles */
.postContainer {
max-width: 1200px;
margin: 0 auto;
padding: 120px 20px 60px;
display: grid;
grid-template-columns: 1fr 300px;
gap: 40px;
}
.postMain {
background-color: $card-bg;
border-radius: 8px;
overflow: hidden;
border: 1px solid $card-border;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.postHeader {
position: relative;
}
.postFeaturedImage {
height: 400px;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
position: relative;
}
.postFeaturedImage::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background: linear-gradient(to top, $card-bg, transparent);
}
.postInfo {
padding: 30px 40px;
}
.postContent {
padding: 0 40px 40px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.8;
color: #e0e0e0;
}
.shareSection {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid $card-border;
}
.shareTitle {
font-size: 1.2rem;
margin-bottom: 15px;
}
.shareButtons {
display: flex;
gap: 15px;
}
.shareButton {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.05);
display: flex;
align-items: center;
justify-content: center;
color: $text-ash;
transition: all 0.3s ease;
}
.shareButton:hover {
background-color: $accent-fire;
color: $text-light;
transform: translateY(-3px);
}
.authorSection {
margin-top: 40px;
padding: 30px;
background-color: rgba(255, 255, 255, 0.02);
border-radius: 8px;
display: flex;
gap: 20px;
align-items: center;
}
.tocList {
list-style: none;
}
.tocItem {
margin-bottom: 12px;
padding-left: 15px;
position: relative;
}
.tocItem::before {
content: "";
position: absolute;
left: 0;
top: 10px;
width: 5px;
height: 5px;
border-radius: 50%;
background-color: $accent-fire;
}
.tocLink {
color: $text-ash;
text-decoration: none;
transition: all 0.3s ease;
font-size: 0.9rem;
line-height: 1.5;
display: block;
}
.tocLink:hover {
color: $accent-fire;
transform: translateX(5px);
}
.tocSubitem {
margin-top: 8px;
padding-left: 15px;
}
.relatedPostsList {
list-style: none;
}
.relatedPostItem {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.relatedPostItem:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.relatedPostLink {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
color: $text-ash;
transition: color 0.3s ease;
}
.relatedPostLink:hover {
color: $accent-fire;
}
.relatedPostThumb {
width: 60px;
height: 60px;
border-radius: 4px;
background-size: cover;
background-position: center;
flex-shrink: 0;
}
.relatedPostInfo {
flex-grow: 1;
}
.relatedPostTitle {
font-size: 0.9rem;
line-height: 1.4;
margin-bottom: 5px;
}
.relatedPostDate {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.5);
}
/* Social grid for contact page */
.socialGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 15px;
margin-top: 1.5rem;
}
.socialItem {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 10px;
padding: 15px;
border-radius: 8px;
background-color: rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
text-decoration: none;
}
.socialItem:hover {
background-color: rgba(255, 100, 0, 0.1);
transform: translateY(-5px);
}
.socialIconLg {
font-size: 1.8rem;
color: $accent-fire;
}
.socialName {
color: $text-ash;
font-size: 0.9rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
@media (max-width: 992px) {
.pageTitle {
font-size: 2.5rem;
}
.timeline:before {
left: 2rem;
}
.timelineItem:nth-child(odd) .timeline-content,
.timelineItem:nth-child(even) .timeline-content {
width: calc(100% - 3rem);
margin-left: 3rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.timelineDot {
left: 2rem;
}
}
@media (max-width: 768px) {
.pageTitle {
font-size: 2rem;
}
.introText {
margin-top: 2rem;
}
}
@media (max-width: 576px) {
.pageTitle {
font-size: 1.8rem;
}
.sectionTitle {
font-size: 1.8rem;
}
.testimonialAuthor {
flex-direction: column;
align-items: center;
text-align: center;
}
.authorInfo {
margin-top: 1rem;
text-align: center;
}
}
+41
View File
@@ -0,0 +1,41 @@
@use './variables.scss' as *;
/* Accordion styling */
.accordion-button {
background-color: $card-bg !important;
color: $text-light !important;
border-color: $card-border !important;
box-shadow: none !important;
padding: 1rem 1.25rem;
font-family: 'Cinzel', serif;
}
.accordion-button:not(.collapsed) {
color: $accent-fire !important;
}
.accordion-button:focus {
border-color: $accent-fire !important;
box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25) !important;
}
.accordion-button::after {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23ff6400'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") !important;
}
.accordion-item {
background-color: transparent;
border: none;
margin-bottom: 1rem;
}
.accordion-body {
background-color: $card-bg;
border: 1px solid $card-border;
border-top: none;
color: $text-ash;
padding: 1.25rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
border-bottom-left-radius: 0.375rem;
border-bottom-right-radius: 0.375rem;
}
+223
View File
@@ -0,0 +1,223 @@
@use "./variables.scss" as *;
/* Meta items */
@mixin metaItem {
.metaItem {
display: inline-flex;
align-items: center;
gap: 6px;
margin-right: 1rem;
color: $text-ash;
font-size: 0.9rem;
}
}
@mixin metaIcon {
.metaIcon {
font-size: 1.2rem;
color: $text-ash;
transition: all 0.3s ease;
}
.metaIcon:hover {
color: $accent-fire;
}
}
/* Read more link */
@mixin readMore {
.readMore {
display: inline-block;
color: $accent-fire;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
position: relative;
}
.readMore::after {
content: "";
margin-left: 5px;
transition: transform 0.3s ease;
}
.readMore:hover::after {
transform: translateX(5px);
}
}
/* Tags */
@mixin postTag {
.postTag {
display: inline-block;
background-color: rgba(255, 100, 0, 0.1);
color: $accent-fire;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 500;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
}
.postTag:hover {
background-color: rgba(255, 100, 0, 0.2);
transform: translateY(-2px);
}
}
@include metaItem;
@include metaIcon;
@include readMore;
@include postTag;
/* Sidebar */
.sidebarWidget {
background-color: $card-bg;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
border: 1px solid $card-border;
}
.widgetTitle {
font-size: 1.2rem;
margin-bottom: 1.25rem;
padding-bottom: 0.625rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
position: relative;
}
.widgetTitle::after {
content: "";
position: absolute;
width: 40px;
height: 2px;
background: $accent-fire;
bottom: -1px;
left: 0;
}
.searchForm .formControl {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid $card-border;
color: $text-light;
}
.searchForm .formControl:focus {
box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25);
border-color: $accent-fire;
}
.searchForm .btn {
background-color: $accent-fire;
border-color: $accent-fire;
}
.searchForm .btn:hover {
background-color: $accent-dragon;
border-color: $accent-dragon;
}
/* Lists in sidebar */
.listGroupItem {
background-color: transparent;
border-color: rgba(255, 255, 255, 0.05);
color: $text-ash;
transition: all 0.3s ease;
}
.listGroupItem:hover {
background-color: rgba(255, 100, 0, 0.1);
}
.listGroupItem a {
color: $text-ash;
text-decoration: none;
display: flex;
justify-content: space-between;
align-items: center;
}
.listGroup-item a:hover {
color: $accent-fire;
}
.categoryCount {
background-color: rgba(255, 100, 0, 0.1);
color: $accent-fire;
padding: 2px 8px;
border-radius: 10px;
font-size: 0.8rem;
}
/* Recent posts in sidebar */
.recentPostItem {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.recentPostItem:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.recentPostThumb {
width: 60px;
height: 60px;
border-radius: 4px;
object-fit: cover;
}
.recentPostInfo {
flex-grow: 1;
}
.recentPostTitle {
font-size: 0.9rem;
line-height: 1.4;
margin-bottom: 5px;
}
.recentPostTitle a {
color: $text-ash;
text-decoration: none;
transition: color 0.3s;
}
.recentPostTitle a:hover {
color: $accent-fire;
}
.recentPostDate {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.5);
}
/* Pagination */
.pageItem .pageLink {
background-color: $card-bg;
border-color: $card-border;
color: $text-ash;
}
.pageItem .pageLink:hover {
background-color: rgba(255, 100, 0, 0.1);
color: $accent-fire;
border-color: $accent-fire;
}
.pageItem.active .pageLink {
background-color: $accent-fire;
border-color: $accent-fire;
color: $text-light;
}
+95
View File
@@ -0,0 +1,95 @@
@use "./variables.scss" as *;
@use "./Blog.module.scss" as Blog;
@include Blog.metaItem;
@include Blog.metaIcon;
@include Blog.readMore;
@include Blog.postTag;
/* Card styling */
.card {
background-color: $card-bg;
border: 1px solid $card-border;
border-radius: 8px;
transition: all 0.3s ease;
margin-bottom: 1.5rem;
overflow: hidden;
}
.card:hover {
transform: translateY(-5px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.cardImgTop {
height: 200px;
object-fit: cover;
}
.featuredImg {
height: 300px;
}
.cardTitle {
font-size: 1.5rem;
margin-bottom: 0.75rem;
}
.cardTitle a {
color: $text-light;
text-decoration: none;
transition: color 0.3s;
}
.cardTitle a:hover {
color: $accent-fire;
}
.cardText {
color: $text-ash;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
}
.featuredCard .cardTitle {
font-size: 1.8rem;
}
.cardFooter {
background-color: rgba(0, 0, 0, 0.2);
border-top: 1px solid $card-border;
}
/* Category badges */
.categoryBadge {
position: absolute;
top: 1rem;
right: 1rem;
background-color: rgba(139, 0, 0, 0.9);
color: $text-light;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
}
.featuredBadge {
position: absolute;
top: 1rem;
left: 1rem;
background: linear-gradient(45deg, $accent-dragon, $accent-fire);
color: $text-light;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
display: flex;
align-items: center;
gap: 5px;
}
+814
View File
@@ -0,0 +1,814 @@
@use './variables' as *;
@use './Blog.module.scss' as Blog;
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Cinzel', serif;
}
.postContainer {
max-width: 1200px;
margin: 0 auto;
padding: 120px 20px 60px;
display: grid;
grid-template-columns: 1fr 300px;
gap: 40px;
}
.postMain {
background-color: $card-bg;
border-radius: 8px;
overflow: hidden;
border: 1px solid $card-border;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.postHeader {
position: relative;
}
.postFeaturedImage {
height: 400px;
background: url('/api/placeholder/900/400') center/cover no-repeat;
position: relative;
}
.postFeaturedImage::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background: linear-gradient(to top, $card-bg, transparent);
}
.postCategory {
position: absolute;
top: 20px;
left: 20px;
background: rgba(139, 0, 0, 0.9);
color: $text-light;
padding: 5px 15px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
backdrop-filter: blur(5px);
z-index: 1;
}
.postInfo {
padding: 30px 40px;
}
.postTitle {
font-size: 2.5rem;
margin-bottom: 20px;
line-height: 1.3;
}
.postMeta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 20px;
font-size: 0.9rem;
color: $text-ash;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid $card-border;
}
.metaItem {
display: flex;
align-items: center;
gap: 6px;
}
.metaIcon {
color: $accent-fire;
}
.postContent {
padding: 0 40px 40px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.8;
color: #e0e0e0;
}
.postContent p {
margin-bottom: 20px;
}
.postContent h2 {
font-size: 1.8rem;
margin: 40px 0 20px;
padding-bottom: 10px;
border-bottom: 1px solid $card-border;
font-family: 'Cinzel', serif;
color: $text-light;
position: relative;
}
.postContent h2::before {
content: "//";
margin-right: 10px;
color: $accent-fire;
font-weight: normal;
font-family: 'Fira Code', monospace;
}
.postContent h3 {
font-size: 1.4rem;
margin: 30px 0 15px;
font-family: 'Cinzel', serif;
color: $text-light;
}
.postContent ul,
.postContent ol {
margin: 0 0 20px 20px;
padding-left: 20px;
}
.postContent li {
margin-bottom: 10px;
}
.postContent a {
color: $accent-fire;
text-decoration: none;
transition: all 0.3s ease;
}
.postContent a:hover {
border-bottom-style: solid;
}
.postContent blockquote {
margin: 30px 0;
padding: 20px 30px;
border-left: 4px solid $accent-fire;
background-color: rgba(255, 100, 0, 0.05);
font-style: italic;
color: $text-ash;
position: relative;
}
.postContent blockquote::before {
content: "\"";
position: absolute;
top: 10px;
left: 10px;
font-size: 3rem;
color: rgba(255, 100, 0, 0.2);
font-family: Georgia, serif;
}
.postContent img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 20px 0;
display: block;
}
.postContent pre {
margin: 20px 0;
border-radius: 8px;
overflow-x: auto;
background-color: $code-bg;
font-family: 'Fira Code', monospace;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
border: 1px solid $card-border;
}
.postContent code {
font-family: 'Fira Code', monospace;
font-size: 0.9rem;
padding: 0 4px;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 3px;
color: $text-light;
}
.postContent pre code {
padding: 0;
background-color: transparent;
border-radius: 0;
}
.postContent .highlight {
margin: 30px 0;
position: relative;
}
.postContent .highlight::before {
content: attr(data-language);
position: absolute;
top: 0;
right: 0;
background-color: $accent-fire;
color: $text-light;
padding: 2px 10px;
font-size: 0.7rem;
border-radius: 0 8px 0 8px;
font-family: 'Fira Code', monospace;
text-transform: uppercase;
z-index: 1;
}
.codeTitle {
background-color: rgba(255, 100, 0, 0.1);
padding: 10px 15px;
border-radius: 8px 8px 0 0;
font-family: 'Fira Code', monospace;
font-size: 0.9rem;
color: $accent-fire;
border-bottom: 1px solid rgba(255, 100, 0, 0.2);
display: flex;
align-items: center;
gap: 10px;
}
.postTags {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid $card-border;
}
.postTag {
background-color: rgba(255, 100, 0, 0.1);
color: $accent-fire;
padding: 5px 12px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 500;
transition: all 0.3s ease;
}
.postTag:hover {
background-color: rgba(255, 100, 0, 0.2);
transform: translateY(-2px);
}
.shareSection {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid $card-border;
}
.shareTitle {
font-size: 1.2rem;
margin-bottom: 15px;
}
.shareButtons {
display: flex;
gap: 15px;
}
.shareButton {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.05);
display: flex;
align-items: center;
justify-content: center;
color: $text-ash;
transition: all 0.3s ease;
}
.shareButton:hover {
background-color: $accent-fire;
color: $text-light;
transform: translateY(-3px);
}
.authorSection {
margin-top: 40px;
padding: 30px;
background-color: rgba(255, 255, 255, 0.02);
border-radius: 8px;
display: flex;
gap: 20px;
align-items: center;
}
.authorAvatar {
width: 80px;
height: 80px;
border-radius: 50%;
overflow: hidden;
border: 3px solid rgba(255, 100, 0, 0.3);
flex-shrink: 0;
}
.authorAvatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.authorInfo {
flex-grow: 1;
}
.authorName {
font-size: 1.2rem;
margin-bottom: 10px;
}
.authorBio {
color: $text-ash;
font-size: 0.9rem;
line-height: 1.6;
}
.authorSocial {
display: flex;
gap: 15px;
margin-top: 15px;
}
.authorSocial a {
color: $text-ash;
transition: all 0.3s ease;
}
.authorSocial a:hover {
color: $accent-fire;
}
.commentsSection {
margin-top: 60px;
}
.commentsTitle {
font-size: 1.5rem;
margin-bottom: 30px;
position: relative;
display: inline-block;
}
.commentsTitle::after {
content: "";
position: absolute;
width: 40px;
height: 2px;
background: $accent-fire;
bottom: -10px;
left: 0;
}
.comment {
padding: 20px 0;
border-bottom: 1px solid $card-border;
display: flex;
gap: 20px;
}
.commentAvatar {
width: 50px;
height: 50px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}
.commentAvatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.commentContent {
flex-grow: 1;
}
.commentAuthor {
font-weight: 600;
margin-bottom: 5px;
}
.commentDate {
font-size: 0.8rem;
color: $text-ash;
margin-bottom: 10px;
}
.commentText {
color: #e0e0e0;
line-height: 1.6;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.commentReply {
margin-top: 10px;
color: $accent-fire;
text-decoration: none;
font-size: 0.9rem;
display: inline-block;
}
.commentForm {
margin-top: 40px;
}
.formTitle {
font-size: 1.3rem;
margin-bottom: 20px;
}
.formGroup {
margin-bottom: 20px;
}
.formControl {
width: 100%;
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid $card-border;
border-radius: 4px;
padding: 12px 15px;
color: $text-light !important;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
resize: vertical;
transition: all 0.3s ease;
}
.formControl:focus {
outline: none;
border-color: $accent-fire;
box-shadow: 0 0 0 2px rgba(255, 100, 0, 0.2);
}
.formRow {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.submitButton {
background-color: $accent-fire;
color: $text-light;
border: none;
border-radius: 4px;
padding: 12px 25px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
font-family: 'Cinzel', serif;
letter-spacing: 1px;
}
.submitButton:hover {
background-color: $accent-dragon;
transform: translateY(-2px);
}
.postSidebar {
position: sticky;
top: 120px;
align-self: start;
}
.sidebarWidget {
background-color: $card-bg;
border-radius: 8px;
padding: 25px;
margin-bottom: 30px;
border: 1px solid $card-border;
}
.widgetTitle {
font-size: 1.2rem;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
position: relative;
}
.widgetTitle::after {
content: "";
position: absolute;
width: 40px;
height: 2px;
background: $accent-fire;
bottom: -1px;
left: 0;
}
.tocList {
list-style: none;
}
.tocItem {
margin-bottom: 12px;
padding-left: 15px;
position: relative;
}
.tocItem::before {
content: "";
position: absolute;
left: 0;
top: 10px;
width: 5px;
height: 5px;
border-radius: 50%;
background-color: $accent-fire;
}
.tocLink {
color: $text-ash;
text-decoration: none;
transition: all 0.3s ease;
font-size: 0.9rem;
line-height: 1.5;
display: block;
}
.tocLink:hover {
color: $accent-fire;
transform: translateX(5px);
}
.tocSubitem {
margin-top: 8px;
padding-left: 15px;
}
.relatedPostsList {
list-style: none;
}
.relatedPostItem {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.relatedPostItem:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.relatedPostLink {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
color: $text-ash;
transition: color 0.3s ease;
}
.relatedPostLink:hover {
color: $accent-fire;
}
.relatedPostThumb {
width: 60px;
height: 60px;
border-radius: 4px;
background-size: cover;
background-position: center;
flex-shrink: 0;
}
.relatedPostInfo {
flex-grow: 1;
}
.relatedPostTitle {
font-size: 0.9rem;
line-height: 1.4;
margin-bottom: 5px;
}
.relatedPostDate {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.5);
}
.footer {
background-color: $primary-dark;
padding: 50px 5% 30px;
text-align: center;
position: relative;
border-top: 1px solid rgba(255, 100, 0, 0.2);
margin-top: 80px;
}
.footerContent {
max-width: 800px;
margin: 0 auto;
}
.socialLinks {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 30px;
}
.socialIcon {
font-size: 1.5rem;
color: $text-ash;
transition: all 0.3s ease;
}
.socialIcon:hover {
color: $accent-fire;
transform: translateY(-5px);
}
.copyright {
color: $text-ash;
font-size: 0.9rem;
margin-top: 20px;
}
.binaryLine {
position: absolute;
font-family: 'Fira Code', monospace;
color: $accent-fire;
font-size: 0.8rem;
opacity: 0.15;
pointer-events: none;
}
@media (max-width: 992px) {
.postContainer {
grid-template-columns: 1fr;
}
.postSidebar {
position: static;
order: -1;
}
.postFeaturedImage {
height: 300px;
}
.postTitle {
font-size: 2rem;
}
.postInfo,
.postContent {
padding: 20px;
}
}
@media (max-width: 768px) {
.navbar {
flex-direction: column;
gap: 15px;
}
.navLinks {
width: 100%;
justify-content: space-around;
font-size: 0.9rem;
}
.postFeaturedImage {
height: 250px;
}
.postTitle {
font-size: 1.8rem;
}
.formRow {
grid-template-columns: 1fr;
gap: 15px;
}
.authorSection {
flex-direction: column;
text-align: center;
align-items: center;
}
.authorSocial {
justify-content: center;
}
}
@media (max-width: 480px) {
.navLinks {
flex-wrap: wrap;
gap: 10px;
}
.postFeaturedImage {
height: 200px;
}
.postTitle {
font-size: 1.5rem;
}
.postMeta {
gap: 10px;
}
.comment {
flex-direction: column;
align-items: flex-start;
}
.commentAvatar {
margin-bottom: 10px;
}
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: $primary-dark;
}
::-webkit-scrollbar-thumb {
background: $card-border;
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: $accent-fire;
}
/* Code line numbers */
.hljsLnNumbers {
user-select: none;
text-align: center;
color: rgba(255, 255, 255, 0.3);
border-right: 1px solid rgba(255, 255, 255, 0.1);
vertical-align: top;
padding-right: 10px !important;
}
.hljsLnCode {
padding-left: 10px !important;
}
/* Tooltips */
.tooltip {
position: relative;
display: inline-block;
color: $accent-fire;
text-decoration: underline;
text-decoration-style: dotted;
cursor: help;
}
.tooltip .tooltipText {
visibility: hidden;
width: 200px;
background-color: $card-bg;
color: $text-light;
text-align: center;
border-radius: 6px;
padding: 10px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.3s;
border: 1px solid $card-border;
font-size: 0.8rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.tooltip .tooltipText::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: $card-border transparent transparent transparent;
}
.tooltip:hover .tooltipText {
visibility: visible;
opacity: 1;
}
+230
View File
@@ -0,0 +1,230 @@
@use './variables' as *;
@use './card.scss' as card;
@include card.card;
/* Card styling */
.contactCard {
position: relative;
border-left: 3px solid $accent-fire;
padding-left: 1.5rem;
}
.contactIcon {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: rgba(255, 100, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
color: $accent-fire;
font-size: 1.5rem;
margin-bottom: 1rem;
}
.contactTitle {
font-size: 1.3rem;
color: $text-light;
margin-bottom: 1rem;
}
.contactContent {
color: $text-ash;
line-height: 1.6;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.contactLink {
color: $accent-fire;
text-decoration: none;
transition: all 0.3s ease;
}
.contactLink:hover {
text-decoration: underline;
color: $accent-ember;
}
/* Button styling */
.btnPrimary {
background: linear-gradient(45deg, $accent-dragon, $accent-fire);
border: none;
padding: 0.75rem 2rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 4px 15px rgba(139, 0, 0, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btnPrimary:hover {
transform: translateY(-3px);
box-shadow: 0 7px 25px rgba(139, 0, 0, 0.5);
background-color: $accent-fire;
background-image: none;
}
.btnPrimary:focus {
box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.4);
}
.btnPrimary::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: 0.6s;
}
.btnPrimary:hover::before {
left: 100%;
}
.btnPrimary:disabled {
background: linear-gradient(45deg, rgba(139, 0, 0, 0.5), rgba(255, 100, 0, 0.5));
transform: none;
}
/* Social links styling */
.socialGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 15px;
margin-top: 1.5rem;
}
.socialItem {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 10px;
padding: 15px;
border-radius: 8px;
background-color: rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
text-decoration: none;
}
.socialItem:hover {
background-color: rgba(255, 100, 0, 0.1);
transform: translateY(-5px);
}
.socialIconLg {
font-size: 1.8rem;
color: $accent-fire;
}
.socialName {
color: $text-ash;
font-size: 0.9rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* Map styling */
.mapContainer {
position: relative;
overflow: hidden;
border-radius: 8px;
border: 1px solid $card-border;
margin-bottom: 2rem;
}
.mapContainer::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 5px;
background: linear-gradient(to right, $accent-dragon, $accent-fire);
z-index: 1;
}
.mapFrame {
width: 100%;
height: 400px;
border: none;
background-color: $card-bg;
filter: grayscale(90%) invert(92%) hue-rotate(180deg);
}
/* Binary background effect */
.binaryLine {
position: absolute;
font-family: 'Fira Code', monospace;
color: $accent-fire;
font-size: 0.8rem;
opacity: 0.15;
pointer-events: none;
}
/* Ember effect */
.emberParticle {
position: absolute;
width: 3px;
height: 3px;
background-color: $accent-fire;
border-radius: 50%;
opacity: 0.6;
pointer-events: none;
z-index: 2;
box-shadow: 0 0 5px $accent-fire;
animation: float 3s linear infinite;
}
@keyframes float {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0.6;
}
100% {
transform: translateY(-100px) rotate(360deg);
opacity: 0;
}
}
/* Table styling */
.table {
color: $text-ash;
margin-bottom: 0;
}
.table td {
padding: 0.5rem 0.25rem;
border-color: rgba(255, 255, 255, 0.05);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
@media (max-width: 768px) {
.schedule-container {
overflow-x: auto;
}
.social-grid {
grid-template-columns: repeat(3, 1fr);
}
.map-frame {
height: 300px;
}
}
@media (max-width: 576px) {
.social-grid {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.form-floating>label {
padding: 0.75rem 0.75rem;
}
}
+227
View File
@@ -0,0 +1,227 @@
.error-container {
position: relative;
z-index: 2;
padding-top: 150px;
padding-bottom: 100px;
}
.error-code {
font-size: 8rem;
font-weight: 700;
background: linear-gradient(to right, var(--accent-fire), var(--text-light));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 1rem;
text-shadow: var(--glow-effect);
position: relative;
}
.error-title {
font-size: 2rem;
margin-bottom: 1.5rem;
color: var(--text-light);
}
.error-text {
font-size: 1.1rem;
color: var(--text-ash);
margin-bottom: 2rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.broken-sword {
font-size: 5rem;
color: var(--accent-fire);
margin-bottom: 2rem;
position: relative;
z-index: 1;
}
.broken-sword::after {
content: "";
position: absolute;
width: 40px;
height: 4px;
background: var(--accent-fire);
transform: rotate(-45deg);
top: 60%;
left: 45%;
box-shadow: var(--glow-effect);
}
.home-button {
background: linear-gradient(45deg, var(--accent-dragon), var(--accent-fire));
border: none;
padding: 0.75rem 2rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 4px 15px rgba(139, 0, 0, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
color: var(--text-light);
text-decoration: none;
border-radius: 4px;
display: inline-flex;
align-items: center;
gap: 10px;
}
.home-button:hover {
transform: translateY(-3px);
box-shadow: 0 7px 25px rgba(139, 0, 0, 0.5);
color: var(--text-light);
}
.home-button::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: 0.6s;
}
.home-button:hover::before {
left: 100%;
}
.error-code-container {
position: relative;
display: inline-block;
}
.error-code-container::before,
.error-code-container::after {
content: "<";
position: absolute;
color: var(--accent-fire);
opacity: 0.6;
font-size: 6rem;
font-family: 'Fira Code', monospace;
}
.error-code-container::before {
left: -4rem;
}
.error-code-container::after {
content: "/>";
right: -6rem;
}
/* Media Queries */
@media (max-width: 992px) {
.timeline:before {
left: 2rem;
}
.timeline-item:nth-child(odd) .timeline-content,
.timeline-item:nth-child(even) .timeline-content {
width: calc(100% - 3rem);
margin-left: 3rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.timeline-dot {
left: 2rem;
}
.post-container {
grid-template-columns: 1fr;
}
.post-sidebar {
position: static;
order: -1;
}
.post-featured-image {
height: 300px;
}
.post-title {
font-size: 2rem;
}
.post-info, .post-content {
padding: 20px;
}
}
@media (max-width: 768px) {
.intro-text {
margin-top: 2rem;
}
.testimonial-author {
flex-direction: column;
align-items: center;
text-align: center;
}
.author-info {
margin-top: 1rem;
text-align: center;
}
.error-code {
font-size: 6rem;
}
.error-title {
font-size: 1.8rem;
}
.broken-sword {
font-size: 4rem;
}
.error-code-container::before,
.error-code-container::after {
font-size: 4rem;
}
.error-code-container::before {
left: -3rem;
}
.error-code-container::after {
right: -4.5rem;
}
}
@media (max-width: 576px) {
.error-code {
font-size: 4rem;
}
.error-title {
font-size: 1.5rem;
}
.broken-sword {
font-size: 3rem;
}
.error-code-container::before,
.error-code-container::after {
font-size: 3rem;
}
.error-code-container::before {
left: -2rem;
}
.error-code-container::after {
right: -3rem;
}
}
+95
View File
@@ -0,0 +1,95 @@
@use './variables.scss' as *;
/* Form styling */
.form-control {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid var(--card-border);
color: var(--text-light);
padding: 0.75rem 0.9375rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.form-control:focus {
background-color: rgba(255, 255, 255, 0.08);
border-color: var(--accent-fire);
box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25);
color: var(--text-light);
}
.form-control::placeholder {
color: rgba(255, 255, 255, 0.3);
}
textarea.form-control {
min-height: 120px;
}
.form-label {
color: var(--text-ash);
font-weight: 600;
margin-bottom: 0.5rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.form-text {
color: rgba(255, 255, 255, 0.5);
}
.form-select {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid var(--card-border);
color: var(--text-light);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ff6400' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.form-select:focus {
background-color: rgba(255, 255, 255, 0.08);
border-color: var(--accent-fire);
box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25);
color: var(--text-light);
}
.form-select option {
background-color: var(--card-bg);
color: var(--text-light);
}
/* Alert styling */
.alert-success {
background-color: rgba(25, 135, 84, 0.1);
border-color: rgba(25, 135, 84, 0.3);
color: #75b798;
}
.alert-danger {
background-color: rgba(220, 53, 69, 0.1);
border-color: rgba(220, 53, 69, 0.3);
color: #ea868f;
}
/* Form validation styling */
.was-validated .form-control:invalid,
.form-control.is-invalid {
border-color: #dc3545;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.was-validated .form-control:valid,
.form-control.is-valid {
border-color: #198754;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.invalid-feedback {
color: #dc3545;
font-size: 0.875em;
margin-top: 0.25rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
+230
View File
@@ -0,0 +1,230 @@
@use './variables' as *;
/* Skills Section */
.skillsSection {
padding: 6rem 0;
background-color: $secondary-dark;
position: relative;
}
/* Hero Section */
.hero {
height: 100vh;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
padding-top: 76px;
}
.hero::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('/api/placeholder/1200/800') center/cover no-repeat;
opacity: 0.15;
filter: brightness(0.5) contrast(1.2);
z-index: -1;
}
.titleMain {
font-size: 4rem;
font-weight: 700;
position: relative;
display: inline-block;
}
.titleMain::before {
content: "< ";
color: $accent-fire;
opacity: 0.8;
}
.titleMain::after {
content: " />";
color: $accent-fire;
opacity: 0.8;
}
.subtitle {
font-size: 1.6rem;
color: $text-ash;
margin-bottom: 2rem;
}
/* Binary Background */
.binaryBg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.07;
overflow: hidden;
}
.binaryLine {
position: absolute;
font-family: 'Fira Code', monospace;
color: $accent-fire;
font-size: 1rem;
animation: fall linear infinite;
opacity: 0.9;
}
@keyframes fall {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(100vh);
}
}
/* Button Styling */
.btnPrimary {
background: linear-gradient(45deg, $accent-dragon, $accent-fire);
border: none;
padding: 0.75rem 2rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 4px 15px rgba(139, 0, 0, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btnPrimary:hover {
transform: translateY(-3px);
box-shadow: 0 7px 25px rgba(139, 0, 0, 0.5);
background-color: $accent-fire;
background-image: none;
}
.btnPrimary::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: all 0.6s ease;
}
.btnPrimary:hover::before {
left: 100%;
}
/* Blinking cursor effect */
.blinkingCursor {
display: inline-block;
width: 10px;
height: 24px;
background-color: $accent-fire;
margin-left: 5px;
animation: blink 1s step-end infinite;
box-shadow: $glow-effect;
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
/* Particles effect */
.particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.particle {
position: absolute;
width: 3px;
height: 3px;
background-color: $accent-fire;
border-radius: 50%;
opacity: 0;
animation: rise linear forwards;
}
@keyframes rise {
0% {
transform: translateY(0) translateX(0);
opacity: 0;
}
10% {
opacity: 0.5;
}
90% {
opacity: 0.5;
}
100% {
transform: translateY(-100px) translateX(var(--x));
opacity: 0;
}
}
// Style for phone
@media (max-width: 992px) {
.title-main {
font-size: 3rem;
}
}
@media (max-width: 768px) {
.hero {
height: 100vh;
padding-top: 50px;
}
.titleMain {
font-size: 2rem;
}
.subtitle {
font-size: 1.4rem;
}
.btnPrimary {
padding: 0.5rem 1.5rem;
font-size: 1.2rem;
}
}
@media (max-width: 576px) {
.title-main {
font-size: 2rem;
}
.subtitle {
font-size: 1rem;
}
.btn-primary {
padding: 0.6rem 1.5rem;
font-size: 0.9rem;
}
}
+58
View File
@@ -0,0 +1,58 @@
@use "./variables.scss" as *;
/* Binary background effect */
.binaryLine {
position: absolute;
font-family: 'Fira Code', monospace;
color: $accent-fire;
font-size: 0.8rem;
opacity: 0.15;
pointer-events: none;
}
/* Page Header */
.pageHeader {
padding-top: 7rem;
padding-bottom: 3rem;
background-color: $secondary-dark;
position: relative;
overflow: hidden;
}
.pageHeader::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: url('/api/placeholder/1200/400');
opacity: 0.15;
filter: brightness(0.3);
z-index: 0;
}
.pageTitle {
font-size: 3rem;
margin-bottom: 1rem;
position: relative;
display: inline-block;
}
.pageTitle::after {
content: "";
position: absolute;
width: 60px;
height: 3px;
background: $accent-fire;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
box-shadow: $glow-effect;
}
.pageSubtitle {
font-size: 1.2rem;
color: $text-ash;
}
+378
View File
@@ -0,0 +1,378 @@
@use './variables' as *;
.filterContainer {
margin-bottom: 2rem;
}
.filterButton {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid $card-border;
color: $text-ash;
padding: 0.5rem 1rem;
margin: 0 0.25rem 0.5rem;
border-radius: 4px;
transition: all 0.3s ease;
}
.filterButton:hover,
.filterButton.active {
background-color: rgba(255, 100, 0, 0.1);
border-color: rgba(255, 100, 0, 0.3);
color: $accent-fire;
}
.filterButton.active {
border-color: $accent-fire;
}
/* Project Cards */
.card {
background-color: $card-bg;
border: 1px solid $card-border;
border-radius: 8px;
transition: all 0.3s ease;
margin-bottom: 1.5rem;
overflow: hidden;
height: 100%;
}
.card:hover {
transform: translateY(-10px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.cardImgTop {
height: 200px;
object-fit: cover;
}
.cardTitle {
font-size: 1.5rem;
margin-bottom: 0.75rem;
}
.cardText {
color: $text-ash;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin-bottom: 1.5rem;
}
.featuredCard {
position: relative;
}
.featuredCard .cardImgTop {
height: 300px;
}
/* Category badges */
.categoryBadgeContainer {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
flex-direction: row-reverse;
gap: 0.5rem;
z-index: 1;
}
.categoryBadge {
background-color: rgba(139, 0, 0, 0.9);
color: $text-light;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
}
.featuredBadge {
position: absolute;
top: 1rem;
left: 1rem;
background: linear-gradient(45deg, $accent-dragon, $accent-fire);
color: $text-light;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
display: flex;
align-items: center;
gap: 5px;
z-index: 1;
}
/* Tech stack */
.techTitle {
font-size: 0.9rem;
margin-bottom: 0.625rem;
color: $text-ash;
font-family: 'Fira Code', monospace;
}
.techStack {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 1.5rem;
}
.techItem {
display: inline-flex;
align-items: center;
gap: 6px;
background-color: rgba(255, 255, 255, 0.05);
padding: 5px 10px;
border-radius: 4px;
font-size: 0.8rem;
font-family: 'Fira Code', monospace;
color: $text-ash;
transition: all 0.3s ease;
}
.techItem:hover {
background-color: rgba(255, 100, 0, 0.1);
color: $accent-fire;
transform: translateY(-2px);
}
.techIcon {
font-size: 1rem;
color: $accent-fire;
}
/* Project links */
.btnOutlineSecondary {
background-color: rgba(255, 255, 255, 0.05);
border-color: $card-border;
color: $text-light;
}
.btnOutlineSecondary:hover {
background-color: rgba(255, 100, 0, 0.1);
border-color: $accent-fire;
color: $text-light;
}
.btnIcon {
margin-right: 0.5rem;
color: $accent-fire;
}
/* View All Button */
.btnPrimary {
background: linear-gradient(45deg, $accent-dragon, $accent-fire);
border: none;
padding: 0.75rem 2rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 4px 15px rgba(139, 0, 0, 0.3);
transition: all 0.3s ease;
}
.btnPrimary:hover {
transform: translateY(-3px);
box-shadow: 0 7px 25px rgba(139, 0, 0, 0.5);
background-color: $accent-fire;
background-image: none;
}
/* Modal Styling */
.modalContent {
background-color: $card-bg;
border: 1px solid $card-border;
color: $text-light;
}
.modalHeader {
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.modalTitle {
color: $text-light;
}
.modalFooter {
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.btnClose {
filter: invert(1) grayscale(100%) brightness(200%);
}
.featuresList {
padding-left: 1.5rem;
color: $text-ash;
}
.featuresList li {
margin-bottom: 0.5rem;
position: relative;
}
.featuresList li::before {
content: "";
color: $accent-fire;
position: absolute;
left: -1rem;
top: 0;
font-size: 1.2rem;
}
.techChip {
display: inline-flex;
align-items: center;
gap: 6px;
background-color: rgba(255, 255, 255, 0.05);
padding: 8px 15px;
border-radius: 20px;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
}
.techChip:hover {
background-color: rgba(255, 100, 0, 0.1);
transform: translateY(-3px);
}
.techChip-icon {
color: $accent-fire;
font-size: 1.1rem;
}
.techChip-name {
color: $text-ash;
font-size: 0.9rem;
font-family: 'Fira Code', monospace;
}
.galleryItem {
height: 150px;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s ease;
cursor: pointer;
margin-bottom: 15px;
}
.galleryItem:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.galleryImage {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Binary background effect */
.binaryLine {
position: absolute;
font-family: 'Fira Code', monospace;
color: $accent-fire;
font-size: 0.8rem;
opacity: 0.15;
pointer-events: none;
}
/* Ember particle effect */
.emberParticle {
position: absolute;
width: 3px;
height: 3px;
background-color: $accent-fire;
border-radius: 50%;
opacity: 0.6;
pointer-events: none;
z-index: 2;
box-shadow: 0 0 5px $accent-fire;
animation: float 3s linear infinite;
}
@keyframes float {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0.6;
}
100% {
transform: translateY(-100px) rotate(360deg);
opacity: 0;
}
}
/* Footer */
.footer {
background-color: $primary-dark;
padding: 3rem 0;
border-top: 1px solid rgba(255, 100, 0, 0.2);
margin-top: 3rem;
}
.socialLinks {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 1.5rem;
}
.socialIcon {
font-size: 1.5rem;
color: $text-ash;
transition: all 0.3s ease;
}
.socialIcon:hover {
color: $accent-fire;
transform: translateY(-5px);
}
.copyright {
color: $text-ash;
font-size: 0.9rem;
text-align: center;
}
/* Responsive adjustments */
@media (max-width: 992px) {
.pageTitle {
font-size: 2.5rem;
}
.featuredCard .row {
flex-direction: column;
}
}
@media (max-width: 768px) {
.pageTitle {
font-size: 2rem;
}
.techStack {
justify-content: center;
}
}
@media (max-width: 576px) {
.pageTitle {
font-size: 1.8rem;
}
.projectLinks {
flex-direction: column;
}
.projectLinks .btn {
margin-bottom: 0.5rem;
}
}
+99
View File
@@ -0,0 +1,99 @@
@use './variables' as *;
/* Skills Section */
.skillsSection {
padding: 6rem 0;
background-color: $secondary-dark;
position: relative;
}
.sectionTitle {
text-align: center;
font-size: 2.5rem;
margin-bottom: 3rem;
position: relative;
display: inline-block;
}
.sectionTitle::after {
content: "";
position: absolute;
width: 80px;
height: 3px;
background: $accent-fire;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
box-shadow: $glow-effect;
}
.card {
background-color: $card-bg;
border: 1px solid $card-border;
border-radius: 8px;
transition: all 0.3s ease;
height: 100%;
position: relative;
overflow: hidden;
}
.card:hover {
transform: translateY(-10px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.card::before {
content: "";
position: absolute;
width: 100%;
height: 5px;
top: 0;
left: 0;
background: linear-gradient(to right, $accent-dragon, $accent-fire);
}
.cardHeader {
background-color: transparent;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 10px;
}
.categoryIcon {
color: $accent-fire;
font-size: 1.5rem;
}
.categoryTitle {
font-size: 1.4rem;
margin-bottom: 0;
}
.skillItem {
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 12px;
}
.skillIcon {
color: $accent-fire;
font-size: 1.5rem;
min-width: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.skillIcon i {
display: inline-flex;
align-items: center;
justify-content: center;
}
.skillName {
color: $text-ash;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
+28
View File
@@ -0,0 +1,28 @@
// Colors
$primary-dark: #0a0a0a;
$secondary-dark: #1a1a1a;
$accent-fire: #ff6400;
$accent-ember: #ff4500;
$accent-dragon: #8B0000;
$text-light: #f5f5f5;
$text-ash: #aaaaaa;
$card-bg: #141414;
$card-border: #2a2a2a;
$code-bg: #1e1e1e;
$font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
// Effects
$glow-effect: 0 0 10px $accent-fire, 0 0 20px rgba(255, 100, 0, 0.4);
// Maps for easy access
$colors: (
"primary-dark": $primary-dark,
"secondary-dark": $secondary-dark,
"accent-fire": $accent-fire,
"accent-ember": $accent-ember,
"accent-dragon": $accent-dragon,
"text-light": $text-light,
"text-ash": $text-ash,
"card-bg": $card-bg,
"card-border": $card-border
);
+35
View File
@@ -0,0 +1,35 @@
@use './variables.scss' as *;
/* Card styling */
@mixin card {
.card {
background-color: $card-bg;
border: 1px solid $card-border;
border-radius: 8px;
transition: all 0.3s ease;
overflow: hidden;
}
.card:hover {
transform: translateY(-7px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.card::before {
content: "";
position: absolute;
width: 100%;
height: 5px;
top: 0;
left: 0;
background: linear-gradient(to right, $accent-dragon, $accent-fire);
z-index: 1;
}
@media (max-width: 576px) {
.card-body {
padding: 1.25rem;
}
}
}
+17 -59
View File
@@ -1,19 +1,8 @@
:root { @use './variables' as *;
--primary-dark: #0a0a0a;
--secondary-dark: #1a1a1a;
--accent-fire: #ff6400;
--accent-ember: #ff4500;
--accent-dragon: #8B0000;
--text-light: #f5f5f5;
--text-ash: #aaaaaa;
--glow-effect: 0 0 10px var(--accent-fire), 0 0 20px rgba(255, 100, 0, 0.4);
--card-bg: #141414;
--card-border: #2a2a2a;
}
body { body {
background-color: var(--primary-dark); background-color: $primary-dark;
color: var(--text-light); color: $text-light;
background-image: url('../images/background.png'); background-image: url('../images/background.png');
background-repeat: round; background-repeat: round;
background-size: 300px 300px; background-size: 300px 300px;
@@ -49,7 +38,7 @@ body::before {
.navbar-toggler { .navbar-toggler {
border: 1px solid rgba(255, 100, 0, 0.5); border: 1px solid rgba(255, 100, 0, 0.5);
color: var(--accent-fire); color: $accent-fire;
} }
.navbar-toggler-icon { .navbar-toggler-icon {
@@ -57,7 +46,7 @@ body::before {
} }
.nav-link { .nav-link {
color: var(--text-light) !important; color: $text-light !important;
position: relative; position: relative;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
transition: color 0.3s; transition: color 0.3s;
@@ -65,7 +54,7 @@ body::before {
.nav-link:hover, .nav-link:hover,
.nav-link.active { .nav-link.active {
color: var(--accent-fire) !important; color: $accent-fire !important;
} }
.nav-link::after { .nav-link::after {
@@ -75,7 +64,7 @@ body::before {
height: 2px; height: 2px;
bottom: 0; bottom: 0;
left: 0; left: 0;
background: linear-gradient(to right, var(--accent-fire), transparent); background: linear-gradient(to right, $accent-fire, transparent);
transition: width 0.3s ease; transition: width 0.3s ease;
} }
@@ -85,10 +74,13 @@ body::before {
} }
/* Logo styling */ /* Logo styling */
.logo-image{
height: 2rem;
}
.logo-icon { .logo-icon {
font-size: 2rem; font-size: 2rem;
color: var(--accent-fire); color: $accent-fire;
text-shadow: var(--glow-effect);
position: relative; position: relative;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -98,7 +90,7 @@ body::before {
.logo-text { .logo-text {
font-weight: 700; font-weight: 700;
font-size: 1.5rem; font-size: 1.5rem;
background: linear-gradient(to right, var(--accent-fire), var(--text-light)); background: linear-gradient(to right, $accent-fire, $text-light);
-webkit-background-clip: text; -webkit-background-clip: text;
background-clip: text; background-clip: text;
color: transparent; color: transparent;
@@ -106,45 +98,11 @@ body::before {
} }
@keyframes fall {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(100vh);
}
}
@keyframes rise {
0% {
transform: translateY(0) translateX(0);
opacity: 0;
}
10% {
opacity: 0.5;
}
90% {
opacity: 0.5;
}
100% {
transform: translateY(-100px) translateX(var(--x));
opacity: 0;
}
}
/* Footer */ /* Footer */
.footer { .footer {
background-color: var(--primary-dark); background-color: $primary-dark;
padding: 3rem 0; padding: 3rem 0;
border-top: 1px solid rgba(255, 100, 0, 0.2); border-top: 1px solid rgba(255, 100, 0, 0.2);
margin-top: 5rem;
} }
.social-links { .social-links {
@@ -156,17 +114,17 @@ body::before {
.social-icon { .social-icon {
font-size: 1.5rem; font-size: 1.5rem;
color: var(--text-ash); color: $text-ash;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.social-icon:hover { .social-icon:hover {
color: var(--accent-fire); color: $accent-fire;
transform: translateY(-5px); transform: translateY(-5px);
} }
.copyright { .copyright {
color: var(--text-ash); color: $text-ash;
font-size: 0.9rem; font-size: 0.9rem;
margin-top: 1rem; margin-top: 1rem;
text-align: center; text-align: center;
+17 -30
View File
@@ -1,20 +1,7 @@
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import styles from '@styles/BlogCard.module.scss';
export interface BlogPostProps { import type {BlogPostProps} from '../untils/BlogPostProps';
id: string;
slug: string;
title: string;
excerpt: string;
image: string;
category: string;
date: string;
author: string;
commentCount: number;
readTime: string;
featured?: boolean;
tags: string[];
content: string;
}
interface BlogCardProps { interface BlogCardProps {
post: BlogPostProps; post: BlogPostProps;
@@ -22,43 +9,43 @@ interface BlogCardProps {
export const BlogCard: React.FC<BlogCardProps> = ({ post }) => { export const BlogCard: React.FC<BlogCardProps> = ({ post }) => {
return ( return (
<div className={`card ${post.featured ? 'featured-card' : ''}`}> <div className={`card ${styles.card} ${post.featured ? styles.featuredCard : ''}`}>
<div className="position-relative"> <div className="position-relative">
<img src={post.image} className={`card-img-top ${post.featured ? 'featured-img' : ''}`} alt={post.title} /> <img src={`${import.meta.env.BASE_URL}${post.image}`} className={`card-img-top ${post.featured ? styles.featuredImg : ''}`} alt={post.title} />
<div className="category-badge">{post.category}</div> <div className={styles.categoryBadge}>{post.category}</div>
{post.featured && ( {post.featured && (
<div className="featured-badge"> <div className={`featured-badge ${styles.featuredBadge}`}>
<i className="fas fa-star"></i> <i className="fas fa-star"></i>
<span>Featured</span> <span>Featured</span>
</div> </div>
)} )}
</div> </div>
<div className="card-body"> <div className={`card-body ${styles.cardBody}`}>
<h2 className="card-title"> <h2 className={`card-title ${styles.cardTitle}`}>
<Link to={`/blog/${post.slug}`}>{post.title}</Link> <Link to={`/blog/${post.slug}`}>{post.title}</Link>
</h2> </h2>
<div className="d-flex meta-items mb-3"> <div className={`d-flex meta-items mb-3`}>
<div className="meta-item"> <div className={`meta-item ${styles.metaItem}`}>
<i className="fas fa-calendar-alt meta-icon"></i> <i className="fas fa-calendar-alt meta-icon"></i>
<span>{post.date}</span> <span>{post.date}</span>
</div> </div>
<div className="meta-item"> <div className={`meta-item ${styles.metaItem}`}>
<i className="fas fa-user meta-icon"></i> <i className={`fas fa-user meta-icon ${styles.metaIcon}`}></i>
<span>{post.author}</span> <span>{post.author}</span>
</div> </div>
<div className="meta-item"> <div className={`meta-item ${styles.metaItem}`}>
<i className="fas fa-comments meta-icon"></i> <i className={`fas fa-comments meta-icon ${styles.metaIcon}`}></i>
<span>{post.commentCount} Comments</span> <span>{post.commentCount} Comments</span>
</div> </div>
</div> </div>
<p className="card-text"> <p className={`card-text ${styles.cardText}`}>
{post.excerpt} {post.excerpt}
</p> </p>
<Link to={`/blog/${post.slug}`} className="read-more">Continue Reading</Link> <Link to={`/blog/${post.slug}`} className={`read-more ${styles.readMore}`}>Continue Reading</Link>
<div className="mt-3"> <div className="mt-3">
{post.tags.map((tag, index) => ( {post.tags.map((tag, index) => (
<span key={index} className="post-tag">{tag}</span> <span key={index} className={`post-tag ${styles.postTag}`}>{tag}</span>
))} ))}
</div> </div>
</div> </div>
+7 -5
View File
@@ -1,4 +1,6 @@
import { useState, type FormEvent } from 'react'; import { useState, type FormEvent } from 'react';
import '@styles/FormControl.scss'
import styles from '@styles/Contact.module.scss'
export const ContactForm = () => { export const ContactForm = () => {
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
@@ -12,7 +14,7 @@ export const ContactForm = () => {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [showSuccess, setShowSuccess] = useState(false); const [showSuccess, setShowSuccess] = useState(false);
const [showError, setShowError] = useState(false); const [showError, ] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value } = e.target; const { name, value } = e.target;
@@ -47,11 +49,11 @@ export const ContactForm = () => {
}; };
return ( return (
<div className="card h-100"> <div className={`card h-100 ${styles.card}`}>
<div className="card-header bg-transparent"> <div className={`card-header bg-transparent ${styles.cardHeader}`}>
<h2 className="mb-0 py-2">Send a Message to the Forge</h2> <h2 className="mb-0 py-2">Send a Message to the Forge</h2>
</div> </div>
<div className="card-body p-4"> <div className={`card-body p-4 ${styles.cardBody}`}>
{/* Success message */} {/* Success message */}
{showSuccess && ( {showSuccess && (
<div className="alert alert-success" role="alert"> <div className="alert alert-success" role="alert">
@@ -176,7 +178,7 @@ export const ContactForm = () => {
<div className="text-center"> <div className="text-center">
<button <button
type="submit" type="submit"
className="btn btn-primary position-relative" className={`btn btn-primary position-relative ${styles.btnPrimary}`}
id="submit-button" id="submit-button"
disabled={isSubmitting} disabled={isSubmitting}
> >
+12 -8
View File
@@ -1,16 +1,20 @@
export const Footer = () => { import {socialLinkData} from '../data/socialLinkData'
const currentYear = new Date().getFullYear() import type {SocialLink} from '../untils/SocialLink'
export const Footer = () => {
const socialLinks: SocialLink[] = socialLinkData;
const currentYear = new Date().getFullYear()
return ( return (
<footer className="footer"> <footer className="footer">
<div className="container"> <div className="container">
<div className="social-links"> <div className="social-links">
<a href="#" className="social-icon"><i className="fab fa-github"></i></a> {socialLinks.map((link, index) => (
<a href="#" className="social-icon"><i className="fab fa-linkedin"></i></a> <a key={index} href={link.url} className="social-icon">
<a href="#" className="social-icon"><i className="fab fa-twitter"></i></a> <i className={link.icon}></i>
<a href="#" className="social-icon"><i className="fab fa-discord"></i></a> </a>
<a href="#" className="social-icon"><i className="fas fa-envelope"></i></a> ))}
<a href="#" className="social-icon"><i className="fab fa-hackerrank"></i></a>
</div> </div>
<p className="copyright">&copy; {currentYear} SecCodeSmith. All rights forged in digital fire.</p> <p className="copyright">&copy; {currentYear} SecCodeSmith. All rights forged in digital fire.</p>
</div> </div>
+2 -11
View File
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { NavLink, useLocation } from 'react-router-dom' import { NavLink, useLocation } from 'react-router-dom'
import logo from '../assets/images/favicon.png'
export const Header = () => { export const Header = () => {
const location = useLocation() const location = useLocation()
@@ -15,17 +16,7 @@ export const Header = () => {
<div className="container"> <div className="container">
<NavLink className="navbar-brand" to="/"> <NavLink className="navbar-brand" to="/">
<div className="logo-icon"> <div className="logo-icon">
<i className="fas fa-hammer"></i> <img src={logo} alt="Logo" className='logo-image' height="100%" />
<i
className="fas fa-dragon"
style={{
fontSize: '1.2rem',
position: 'absolute',
top: 0,
right: '-12px',
color: 'var(--accent-dragon)'
}}
></i>
</div> </div>
<div className="logo-text">SecCodeSmith</div> <div className="logo-text">SecCodeSmith</div>
</NavLink> </NavLink>
+10 -8
View File
@@ -1,3 +1,5 @@
import style from '@styles/PageHeader.module.scss'
interface PageHeaderProps { interface PageHeaderProps {
title: string; title: string;
subtitle?: string; subtitle?: string;
@@ -5,17 +7,17 @@ interface PageHeaderProps {
export const PageHeader: React.FC<PageHeaderProps> = ({ title, subtitle }) => { export const PageHeader: React.FC<PageHeaderProps> = ({ title, subtitle }) => {
return ( return (
<section className="page-header text-center"> <section className={`text-center ${style.pageHeader}`}>
<div className="container position-relative"> <div className={`container position-relative`}>
<h1 className="page-title">{title}</h1> <h1 className={style.pageTitle}>{title}</h1>
{subtitle && <p className="page-subtitle mt-4">{subtitle}</p>} {subtitle && <p className={`mt-4 ${style.pageSubtitle}`}>{subtitle}</p>}
{/* Binary background effect */} {/* Binary background effect */}
<div className="binary-bg"> <div className="binary-bg">
<div className="binary-line" style={{ top: '20%', left: '10%' }}>01001100 01101111 01110010 01100101 01101101</div> <div className={style.binaryLine} style={{ top: '20%', left: '10%' }}>01001100 01101111 01110010 01100101 01101101</div>
<div className="binary-line" style={{ bottom: '30%', right: '15%' }}>01001001 01110000 01110011 01110101 01101101</div> <div className={style.binaryLine} style={{ bottom: '30%', right: '15%' }}>01001001 01110000 01110011 01110101 01101101</div>
<div className="binary-line" style={{ top: '50%', left: '80%' }}>01000100 01101111 01101100 01101111 01110010</div> <div className={style.binaryLine} style={{ top: '50%', left: '80%' }}>01000100 01101111 01101100 01101111 01110010</div>
<div className="binary-line" style={{ bottom: '10%', left: '40%' }}>01010011 01101001 01110100 00100000 01000001 01101101 01100101 01110100</div> <div className={style.binaryLine} style={{ bottom: '10%', left: '40%' }}>01010011 01101001 01110100 00100000 01000001 01101101 01100101 01110100</div>
</div> </div>
</div> </div>
</section> </section>
+112 -61
View File
@@ -1,90 +1,141 @@
import { useState } from 'react' import React, { useEffect, useState } from 'react'
export interface ProjectTech { import style from '@styles/Project.module.scss'
name: string; import type {ProjectTech, Category, ProjectProps } from '../untils/ProjectProps'
icon: string;
}
export interface ProjectProps {
id: string;
title: string;
description: string;
image: string;
category: string[];
featured?: boolean;
technologies: ProjectTech[];
links: {
github?: string;
demo?: string;
};
}
interface ProjectCardProps { interface ProjectCardProps {
project: ProjectProps; project: ProjectProps;
onOpenDetails: (id: string) => void; onOpenDetails: (id: string) => void;
} }
export const ProjectCard: React.FC<ProjectCardProps> = ({ project, onOpenDetails }) => { const ProjectCardTech: React.FC<{ technology: ProjectTech[] }> = ({ technology }) => (
const [, setHovered] = useState(false);
return (
<div
className={`card h-100 ${project.featured ? 'featured-card' : ''}`}
data-category={project.category.join(',')}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<div className="position-relative">
<img src={project.image} className="card-img-top" alt={project.title} />
<div className="category-badge">{project.category[0]}</div>
{project.featured && (
<div className="featured-badge">
<i className="fas fa-star"></i>
<span>Featured</span>
</div>
)}
</div>
<div className="card-body d-flex flex-column">
<h3 className="card-title">{project.title}</h3>
<p className="card-text">
{project.description}
</p>
<div> <div>
<h4 className="tech-title">// TECHNOLOGIES_USED</h4> <h4 className={style.techTitle}>// TECHNOLOGIES_USED</h4>
<div className="tech-stack"> <div className={style.techStack}>
{project.technologies.map((tech, index) => ( {technology.map((tech, index) => (
<span className="tech-item" key={index}> <span className={`${style.techItem}`} key={index}>
<i className={tech.icon}></i> <i className={`${tech.icon} ${style.techIcon}`}></i>
<span>{tech.name}</span> <span>{tech.name}</span>
</span> </span>
))} ))}
</div> </div>
</div> </div>
<div className="mt-auto d-flex gap-2 flex-wrap project-links"> )
const CategoryBadge: React.FC<{ category: Category[] }> = ({ category }) => (
< div className={style.categoryBadgeContainer} >
{
category.map((cat, index) => (
<span key={index} className={`${style.categoryBadge}`}>
{cat.shortName}
</span>
))}
</div>
)
const ProjectLinks: React.FC<{ links: { github?: string; demo?: string }, id: string, onOpenDetails: (id: string) => void }> = ({ links, id, onOpenDetails }) => (
<div className={`mt-auto d-flex gap-2 flex-wrap project-links ${style.projectLinks}`}>
<button <button
className="btn btn-outline-secondary" className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`}
onClick={() => onOpenDetails(project.id)} onClick={() => onOpenDetails(id)}
> data-bs-toggle="modal" data-bs-target="#shadowguardianModal">
<i className="fas fa-info-circle me-2"></i> <i className={`fas fa-info-circle ${style.btnIcon}`} ></i>
Project Details Project Details
</button> </button>
{project.links.github && ( {links.github && (
<a href={project.links.github} className="btn btn-outline-secondary"> <a href={links.github} className={`btn btn-outline-secondary ${style.btnOutlineSecondary} `}>
<i className="fab fa-github me-2"></i> <i className={`fab fa-github ${style.btnIcon}`} ></i>
View Code View Code
</a> </a>
)} )}
{project.links.demo && ( {links.demo && (
<a href={project.links.demo} className="btn btn-outline-secondary"> <a href={links.demo} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`}>
<i className="fas fa-external-link-alt me-2"></i> <i className={`fas fa-external-link-alt ${style.btnIcon}`}></i>
Live Demo Live Demo
</a> </a>
)} )}
</div> </div>
)
export const ProjectCard: React.FC<ProjectCardProps> = ({ project, onOpenDetails }) => {
const [, setHovered] = useState(false);
useEffect(() => {
const createEmbers = () => {
const embersContainer = document.querySelectorAll(`.${style.cardImgTop}`);
embersContainer.forEach((container) => {
for (let i = 0; i < 5; i++) {
const ember = document.createElement('div');
ember.className = style.emberParticle;
ember.style.left = `${Math.random() * 100}%`;
ember.style.bottom = '0';
ember.style.animationDuration = `${Math.random() * 2 + 3}s`;
ember.style.animationDelay = `${Math.random() * 3}s`;
container.appendChild(ember);
}
});
};
createEmbers();
}, []);
if (project.featured) {
return (
<div className={`"col-12 mb-4`}>
<div
className={`card ${style.card} ${style.featuredCard}`}
data-category={project.category.join(',')}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<div className={`row g-0`}>
<div className={`col-md-6 position-relative`}>
<img src={project.image} className={`img-fluid rounded-start h-100 w-100 object-fit-cover`} alt={project.title} />
<div className={style.featuredBadge}>
<i className="fas fa-star"></i>
<span>Featured</span>
</div>
<CategoryBadge category={project.category} />
</div>
<div className={`col-md-6`}>
<div className={`card-body d-flex flex-column h-100 ${style.cardBody}`}>
<h3 className={`${style.cardTitle} card-title`}>{project.title}</h3>
<p className={`${style.cardText} card-text`}>
{project.description}
</p>
<ProjectCardTech technology={project.technologies} />
<ProjectLinks links={project.links} id={project.id} onOpenDetails={onOpenDetails} />
</div>
</div>
</div>
</div> </div>
</div> </div>
) )
} }
return (
<div className={`col-md-6 col-lg-4 mb-4`}>
<div className={`card ${style.card} h-100`}
data-category={project.category.join(',')}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}>
<div className={`position-relative`}>
<img src={`${import.meta.env.BASE_URL}${project.image}`} className={style.cardImgTop} alt={project.title} />
<CategoryBadge category={project.category} />
</div>
<div className={`card-body d-flex flex-column ${style.cardBody}`}>
<h3 className={`${style.cardTitle} card-title`}>{project.title}</h3>
<p className={`${style.cardText} card-text`}>
{project.description}
</p>
<ProjectCardTech technology={project.technologies} />
<ProjectLinks links={project.links} id={project.id} onOpenDetails={onOpenDetails} />
</div>
</div>
</div>
)
};
+126 -23
View File
@@ -1,56 +1,159 @@
import type { ProjectProps } from './ProjectCard'; import type { ProjectProps } from '../untils/ProjectProps';
import style from '@styles/Project.module.scss'
interface ProjectModalProps { interface ProjectModalProps {
project: ProjectProps | null; project: ProjectProps | null;
onClose: () => void;
} }
export const ProjectModal: React.FC<ProjectModalProps> = ({ project, onClose }) => { export const ProjectModal: React.FC<ProjectModalProps> = ({ project }) => {
if (!project) return null; if (!project) return null;
return ( return (
<div className="modal fade show" style={{ display: 'block' }} tabIndex={-1}> <div className="modal fade" id="shadowguardianModal" tabIndex={-1} aria-labelledby="shadowguardianModalLabel" aria-hidden="true">
<div className="modal-dialog modal-xl modal-dialog-scrollable"> <div className="modal-dialog modal-xl modal-dialog-scrollable">
<div className="modal-content"> <div className={`modal-content ${style.modalContent}`}>
<div className="modal-header"> <div className={`modal-header ${style.modalHeader}`}>
<h5 className="modal-title">{project.title}</h5> <h5 className={style.modalHeader} id="shadowguardianModalLabel">
<button type="button" className="btn-close" onClick={onClose}></button> {project.title}
</h5>
<button type="button" className={`btn-close ${style.btnClose}`} data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div className="modal-body"> <div className={`modal-body ${style.modalBody}`}>
<div className="mb-4"> <div className="mb-4">
<img src={project.image} className="img-fluid rounded mb-3" alt={project.title} /> <img src={project.image} className="img-fluid rounded mb-3" alt={project.title} />
<div className={`row ${style.row}`}>
<div className="col-md-8">
{
project.projectDetails ? project.projectDetails.descriptions?.map((desc, index) => (
<p key={index} className="mb-3">{desc}</p>
)) :
<p>{project.description}</p>
}
</div>
<div className="col-md-4">
<div className={`card h-100 ${style.card}`}>
<div className={`card-header ${style.cardHeader}`}>
<h5 className="mb-0">Project Overview</h5>
</div>
<div className="card-body">
{project.projectDetails && project.projectDetails.startDate && project.projectDetails.endDate ? (
<p><strong>Duration:</strong> {new Date(project.projectDetails.startDate).toLocaleDateString()} - {new Date(project.projectDetails.endDate).toLocaleDateString()}</p>
) : (
<p><strong>Duration:</strong> Ongoing</p>
)
}
{
project.projectDetails && project.projectDetails.role ? (
<p><strong>Role:</strong> {project.projectDetails.role}</p>
) : (
<p><strong>Role:</strong> Developer</p>
)
}
{
project.projectDetails && project.projectDetails.client ? (
<p><strong>Client:</strong> {project.projectDetails.client}</p>
) : (
<p><strong>Client:</strong> Internal Project</p>
)
}
</div>
</div>
</div>
</div> </div>
<p>{project.description}</p>
{
project.projectDetails?.keyFeatures && project.projectDetails.keyFeatures.length > 0 && (
<>
<h4 className="mt-4 mb-3">Key Features</h4>
<ul className={style.featuresList}>
{
project.projectDetails?.keyFeatures.map((feature, index) => (
<li key={index}>
{feature}
</li>
))
}
</ul>
</>
)
}
<h4 className="mt-4 mb-3">Technologies Used</h4> <h4 className="mt-4 mb-3">Technologies Used</h4>
<div className="mb-4"> <div className="mb-4"></div>
{project.technologies.map((tech, index) => ( {
<span className="tech-chip" key={index}> project.projectDetails?.fullTechStack && project.projectDetails?.fullTechStack?.length > 0 ? (
<i className={`${tech.icon} tech-chip-icon`}></i> <>
{project.projectDetails.fullTechStack.map((tech, index) => (
<span key={index} className={`tech-chip ${style.techChip}`}>
<i className={`${tech.icons} tech-chip-icon`}></i>
<span className="tech-chip-name">{tech.name}</span> <span className="tech-chip-name">{tech.name}</span>
</span> </span>
))} ))}
</>
) : (
<>
{project.technologies && project.technologies.length > 0 ? (
project.technologies.map((tech, index) => (
<span key={index} className={`tech-chip ${style.techChip}`}>
<i className={`${tech.icon} tech-chip-icon`}></i>
<span className="tech-chip-name">{tech.name}</span>
</span>
))
) : (
<p className="text-muted">No technologies listed.</p>
)
}
</>
)
}
</div>
{
project.projectDetails?.gallery && project.projectDetails?.gallery.length > 0 && (
<>
<h4 className="mt-4 mb-3">Gallery</h4>
<div className="row">
{
project.projectDetails.gallery.map((image, index) => (
<div className="col-md-4 col-6">
<div key={index} className={`gallery-item ${style.galleryItem}`}>
<img src={image} alt={`Gallery Image ${index + 1}`} className={`gallery-image ${style.galleryImage}`}></img>
</div> </div>
</div> </div>
<div className="modal-footer"> ))
}
</div>
</>
)
}
</div>
<div className={`modal-footer ${style.modalFooter}`}>
{project.links.github && ( {project.links.github && (
<a href={project.links.github} className="btn btn-outline-secondary"> <a href={project.links.github} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`} target="_blank" rel="noopener noreferrer">
<i className="fab fa-github me-2"></i>Source Code <i className="fab fa-github btn-icon"></i>Source Code
</a> </a>
)} )}
{project.links.demo && ( {project.links.demo && (
<a href={project.links.demo} className="btn btn-outline-secondary"> <a href={project.links.demo} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`} target="_blank" rel="noopener noreferrer">
<i className="fas fa-external-link-alt me-2"></i>Live Demo <i className="fas fa-external-link-alt btn-icon"></i>Live Demo
</a>
)}
{project.links.documentation && (
<a href={project.links.documentation} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`} target="_blank" rel="noopener noreferrer">
<i className="fas fa-book btn-icon"></i>Documentation
</a> </a>
)} )}
<button type="button" className="btn btn-secondary" onClick={onClose}>Close</button> <button type="button" className={`btn btn-secondary`} data-bs-dismiss="modal">Close</button>
</div> </div>
</div> </div>
</div> </div>
<div className="modal-backdrop fade show"></div>
</div> </div>
); );
} };
+33
View File
@@ -0,0 +1,33 @@
import styles from '@styles/SkillCard.module.scss';
import React from 'react';
import type {SkillCardProps} from '../untils/SkillCardProps';
const SkillCard: React.FC<SkillCardProps> = ({ categoryTitle, categoryIcon, skills }) => {
return (
<div className="col-md-6 col-lg-4">
<div className={`card h-100 ${styles.card}`}>
<div className={`card-header ${styles.cardHeader}`}>
<i className={`fas ${categoryIcon} category-icon ${styles.categoryIcon}`}></i>
<h3 className={`${styles.categoryTitle}`}>{categoryTitle}</h3>
</div>
<div className="card-body">
{skills.length > 0 ? (
<ul className="list-unstyled mb-0">
{skills.map((skill, index) => (
<li key={index} className={styles.skillItem}>
<span className={styles.skillIcon}><i className={`${skill.icon ?? ""}`}></i></span>
<span className={styles.skillName}>{skill.name}</span>
</li>
))}
</ul>
) : (
<p>No skills listed in this category.</p>
)}
</div>
</div>
</div>
);
};
export default SkillCard;
-135
View File
@@ -1,127 +1,3 @@
/* About Page Styles */
.profile-image {
width: 100%;
aspect-ratio: 1/1;
border-radius: 8px;
overflow: hidden;
border: 3px solid rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
position: relative;
}
.profile-image::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, rgba(255, 100, 0, 0.2), transparent);
z-index: 1;
}
.profile-image img {
width: 100%;
height: 100%;
object-fit: cover;
filter: contrast(1.1) brightness(0.9);
}
.intro-text {
font-size: 1.1rem;
line-height: 1.8;
color: var(--text-ash);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.intro-text p {
margin-bottom: 1.25rem;
}
.intro-text strong {
color: var(--text-light);
}
.intro-text .accent {
color: var(--accent-fire);
}
.connect-link {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 20px;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
text-decoration: none;
color: var(--text-light);
transition: all 0.3s ease;
border: 1px solid var(--card-border);
margin-right: 0.5rem;
margin-bottom: 0.5rem;
}
.connect-link:hover {
background-color: rgba(255, 100, 0, 0.1);
transform: translateY(-3px);
border-color: var(--accent-fire);
color: var(--text-light);
}
.connect-icon {
color: var(--accent-fire);
}
/* Value cards */
.value-card {
background-color: var(--card-bg);
border-radius: 8px;
padding: 1.875rem;
border: 1px solid var(--card-border);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
text-align: center;
height: 100%;
}
.value-card:hover {
transform: translateY(-5px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.value-icon {
font-size: 2.5rem;
color: var(--accent-fire);
margin-bottom: 1.25rem;
}
.value-title {
font-size: 1.3rem;
margin-bottom: 1rem;
position: relative;
display: inline-block;
color: var(--text-light);
}
.value-title::after {
content: "";
position: absolute;
width: 40px;
height: 2px;
background: var(--accent-fire);
bottom: -5px;
left: 50%;
transform: translateX(-50%);
}
.value-text {
color: var(--text-ash);
line-height: 1.6;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* Skills card */ /* Skills card */
.skill-card { .skill-card {
background-color: var(--card-bg); background-color: var(--card-bg);
@@ -532,17 +408,6 @@
transform: translateY(-5px); transform: translateY(-5px);
} }
.social-icon-lg {
font-size: 1.8rem;
color: var(--accent-fire);
}
.social-name {
color: var(--text-ash);
font-size: 0.9rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* 404 page styles */ /* 404 page styles */
.error-container { .error-container {
position: relative; position: relative;
+7
View File
@@ -0,0 +1,7 @@
import type {AboutProps} from '../untils/AboutProps'
const url = `${import.meta.env.BASE_URL}data/AboutProps.json`;
const res = await fetch(url);
if (!res.ok) throw new Error(`Failed to fetch about props: ${res.status}`);
export const aboutProps: AboutProps = await res.json();
+5 -131
View File
@@ -1,133 +1,7 @@
import type { BlogPostProps } from "../components/BlogCard"; import type {BlogPostProps} from '../untils/BlogPostProps'
export const blogPostsData: BlogPostProps[] = [ const url = `${import.meta.env.BASE_URL}data/BlogPosts.json`;
{ const res = await fetch(url);
id: "1", if (!res.ok) throw new Error(`Failed to fetch blog posts: ${res.status}`);
slug: "summoning-digital-entities-stm32-techniques",
title: "Summoning Digital Entities: Advanced STM32 Techniques for IoT Communication",
excerpt: "Delve into the arcane arts of STM32 microcontroller programming as we explore advanced techniques for establishing robust communication between IoT devices. This comprehensive guide reveals how to optimize I2C, SPI, and UART protocols for maximum efficiency in your embedded systems projects.",
image: "/images/blog/stm32-communication.jpg",
category: "Embedded Systems",
date: "May 5, 2025",
author: "SecCodeSmith",
commentCount: 12,
readTime: "15 min read",
featured: true,
tags: ["STM32", "Embedded", "IoT", "Communication Protocols"],
content: `
<p>In the digital forge where hardware meets software, the STM32 microcontroller family stands as a powerful conduit for summoning digital entities into our physical realm. Today, we shall delve into advanced techniques for establishing robust communication between these entities through the mystical arts of embedded systems programming.</p>
<p>The realm of IoT demands reliable, efficient communication protocols that can withstand the chaotic nature of real-world environments. Through careful implementation and optimization of the STM32's communication peripherals, we can craft resilient connections that bridge the gap between digital and physical domains.</p> export const blogPostsData: BlogPostProps[] = await res.json();
<h2>The Three Elemental Protocols: I2C, SPI, and UART</h2>
<p>Before we forge ahead into advanced territory, let us briefly recall the elemental forces at our disposal. Each protocol serves as a channel through which our digital entities communicate, each with its unique strengths and limitations:</p>
<ul>
<li><strong>I2C (Inter-Integrated Circuit)</strong> - A two-wire interface requiring minimal physical connections, ideal for communication with multiple peripheral devices over short distances.</li>
<li><strong>SPI (Serial Peripheral Interface)</strong> - A faster, full-duplex protocol with dedicated select lines for each peripheral, allowing simultaneous transmission and reception of data.</li>
<li><strong>UART (Universal Asynchronous Receiver-Transmitter)</strong> - A simple two-wire asynchronous interface, perfect for direct device-to-device communication without a shared clock.</li>
</ul>
`
},
{
id: "2",
slug: "forging-neural-networks-pytorch-blacksmith-approach",
title: "Forging Neural Networks with PyTorch: A Blacksmith's Approach",
excerpt: "Learn how to craft powerful neural networks using PyTorch, approaching the process with the precision and artistry of a master blacksmith. This tutorial combines theory with practical implementation.",
image: "/images/blog/neural-networks-pytorch.jpg",
category: "Machine Learning",
date: "April 28, 2025",
author: "SecCodeSmith",
commentCount: 8,
readTime: "12 min read",
tags: ["PyTorch", "Machine Learning", "Neural Networks", "Python"],
content: `
<p>The art of forging neural networks requires both scientific precision and creative intuition—much like the work of a master blacksmith. In this guide, we'll explore how to craft robust neural network architectures using PyTorch, one of the most flexible deep learning frameworks available.</p>
<h2>Preparing the Forge: Setting Up Your PyTorch Environment</h2>
<p>Before we begin crafting our neural networks, we need to prepare our digital forge with the proper tools and materials...</p>
`
},
{
id: "3",
slug: "dark-arts-memory-management-cpp",
title: "The Dark Arts of Memory Management in C++",
excerpt: "Explore the shadowy realm of manual memory management in C++. From smart pointers to custom allocators, master techniques that will prevent memory leaks and boost performance.",
image: "/images/blog/cpp-memory.jpg",
category: "C++",
date: "April 22, 2025",
author: "SecCodeSmith",
commentCount: 15,
readTime: "18 min read",
tags: ["C++", "Memory Management", "Performance", "Smart Pointers"],
content: `
<p>Memory management in C++ is often viewed as a mysterious and perilous art, filled with potential hazards for the unwary programmer. Yet, mastering this art grants you unparalleled control over your program's resources and performance.</p>
<h2>The Ancient Runes: Raw Pointers and Manual Allocation</h2>
<p>At the foundation of C++ memory management lie raw pointers and manual allocation through the new and delete operators...</p>
`
},
{
id: "4",
slug: "docker-containment-spells-isolating-digital-entities",
title: "Docker Containment Spells: Isolating Digital Entities",
excerpt: "Master the containment of digital entities with Docker. This guide covers advanced containerization techniques for creating isolated, reproducible environments for your applications.",
image: "/images/blog/docker-containers.jpg",
category: "DevOps",
date: "April 15, 2025",
author: "SecCodeSmith",
commentCount: 6,
readTime: "14 min read",
tags: ["Docker", "Containerization", "DevOps", "Microservices"],
content: `
<p>In the realm of modern software development, Docker has emerged as a powerful tool for containing and isolating digital entities. Like a master sorcerer binding spirits, Docker allows us to encapsulate applications and their dependencies into sealed containers, ensuring consistent behavior across different environments.</p>
<h2>The Fundamentals of Container Magic</h2>
<p>At its core, Docker utilizes Linux kernel features like namespaces and control groups to create isolated environments that share the host's OS kernel but are otherwise separated from one another...</p>
`
},
{
id: "5",
slug: "blazor-incantations-building-reactive-web-uis-dotnet",
title: "Blazor Incantations: Building Reactive Web UIs with .NET",
excerpt: "Harness the power of Blazor to create dynamic, responsive web interfaces using C# and .NET. Learn component-based architecture and state management for modern web applications.",
image: "/images/blog/blazor-web.jpg",
category: "Web Dev",
date: "April 8, 2025",
author: "SecCodeSmith",
commentCount: 9,
readTime: "16 min read",
tags: ["Blazor", ".NET", "Web Development", "C#"],
content: `
<p>Blazor represents a paradigm shift in web development, allowing .NET developers to craft rich, interactive web applications using C# instead of JavaScript. This modern framework brings the power and elegance of .NET to the browser, opening new possibilities for web development.</p>
<h2>Understanding the Blazor Magic</h2>
<p>Blazor comes in two flavors: Blazor WebAssembly, which runs directly in the browser via WebAssembly, and Blazor Server, which executes on the server and communicates with the client via SignalR...</p>
`
},
{
id: "6",
slug: "esp32-ethereal-connections-wireless-sensor-network",
title: "ESP32 Ethereal Connections: Building a Wireless Sensor Network",
excerpt: "Create a mesh of interconnected ESP32 devices that communicate wirelessly. This tutorial covers WiFi, Bluetooth, and custom RF protocols for building resilient IoT systems.",
image: "/images/blog/esp32-network.jpg",
category: "IoT",
date: "March 30, 2025",
author: "SecCodeSmith",
commentCount: 11,
readTime: "20 min read",
tags: ["ESP32", "IoT", "Wireless", "Sensor Networks"],
content: `
<p>The ESP32 microcontroller has revolutionized the world of IoT with its powerful wireless capabilities, making it the perfect foundation for building ethereal connections between sensors and systems. In this guide, we'll explore how to craft a resilient wireless sensor network using these versatile devices.</p>
<h2>The Mystical Properties of ESP32</h2>
<p>Before diving into network architecture, let's examine what makes the ESP32 uniquely suited for wireless sensor networks. With dual-core processors, integrated WiFi and Bluetooth, and numerous GPIO pins...</p>
`
}
];
+7
View File
@@ -0,0 +1,7 @@
import type {ContactProps} from '../untils/ContactProps'
const url = `${import.meta.env.BASE_URL}data/Contact.json`;
const res = await fetch(url);
if (!res.ok) throw new Error(`Failed to fetch blog posts: ${res.status}`);
export const contactData: ContactProps = await res.json();
+41 -98
View File
@@ -1,105 +1,48 @@
import type { ProjectProps } from "../components/ProjectCard"; import type { ProjectProps, Category } from "../untils/ProjectProps";
export const projectsData: ProjectProps[] = [ const url = `${import.meta.env.BASE_URL}data/ProjectProps.json`;
{ const res = await fetch(url);
id: "shadowguardian", if (!res.ok) throw new Error(`Failed to fetch projects: ${res.status}`);
title: "ShadowGuardian: Secure IoT Monitoring System",
description: "A comprehensive security monitoring system built on STM32 microcontrollers with end-to-end encryption and low-power operation. Integrates with home automation systems and provides real-time alerts through a mobile application.", type CategoryMap = Record<string, Category>
image: "/images/projects/shadowguardian.jpg", export const Categories: CategoryMap = {
category: ["iot", "embedded"], Embedded: {
featured: true, fullName: "Embedded Systems",
technologies: [ shortName: "Embedded",
{ name: "C++", icon: "devicon-cplusplus-plain tech-icon" }, icon: "fas fa-microchip",
{ name: "STM32", icon: "fas fa-microchip tech-icon" },
{ name: "ESP32", icon: "fas fa-wifi tech-icon" },
{ name: "Encryption", icon: "fas fa-shield-alt tech-icon" },
{ name: "React Native", icon: "devicon-react-original tech-icon" }
],
links: {
github: "https://github.com/SecCodeSmith/shadowguardian",
demo: "https://shadowguardian.demo.com"
}
}, },
{ Web: {
id: "pyrosight", fullName: "Web Development",
title: "PyroSight: Thermal Anomaly Detection", shortName: "Web",
description: "An intelligent system that uses computer vision and thermal imaging to detect anomalies in industrial equipment. Employs PyTorch for deep learning to predict potential failures before they occur.", icon: "fas fa-code",
image: "/images/projects/pyrosight.jpg",
category: ["ml", "iot"],
technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "PyTorch", icon: "devicon-pytorch-original tech-icon" },
{ name: "OpenCV", icon: "fas fa-camera tech-icon" },
{ name: "Raspberry Pi", icon: "fas fa-microchip tech-icon" }
],
links: {
github: "https://github.com/SecCodeSmith/pyrosight"
}
}, },
{ ML: {
id: "nexusflow", fullName: "Machine Learning",
title: "NexusFlow: DevOps Dashboard", shortName: "ML",
description: "A real-time monitoring dashboard for DevOps teams that visualizes CI/CD pipelines, infrastructure metrics, and deployment statuses. Built with Blazor for seamless updates without page refreshes.", icon: "fas fa-robot",
image: "/images/projects/nexusflow.jpg",
category: ["web"],
technologies: [
{ name: "C#", icon: "devicon-csharp-plain tech-icon" },
{ name: "Blazor", icon: "devicon-dotnetcore-plain tech-icon" },
{ name: "Entity Framework", icon: "fas fa-database tech-icon" },
{ name: "Docker", icon: "devicon-docker-plain tech-icon" }
],
links: {
github: "https://github.com/SecCodeSmith/nexusflow",
demo: "https://nexusflow.demo.com"
}
}, },
{ IoT: {
id: "etherwhisper", fullName: "Internet of Things",
title: "EtherWhisper: Secure Communication Node", shortName: "IoT",
description: "A low-power, long-range communication node for IoT networks with military-grade encryption and mesh capabilities. Perfect for remote sensing applications in harsh environments.", icon: "fas fa-network-wired",
image: "/images/projects/etherwhisper.jpg",
category: ["embedded", "iot"],
technologies: [
{ name: "C", icon: "devicon-c-plain tech-icon" },
{ name: "STM32", icon: "fas fa-memory tech-icon" },
{ name: "LoRa", icon: "fas fa-broadcast-tower tech-icon" },
{ name: "AES-256", icon: "fas fa-shield-alt tech-icon" }
],
links: {
github: "https://github.com/SecCodeSmith/etherwhisper"
}
}, },
{ Sec: {
id: "dataforge", fullName: "Cybersecurity",
title: "DataForge: Advanced Analytics Platform", shortName: "Security",
description: "A comprehensive data analytics platform with interactive visualization tools and automated reporting features. Integrates with various data sources and provides ML-powered insights.", icon: "fas fa-shield-alt",
image: "/images/projects/dataforge.jpg",
category: ["web"],
technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "Django", icon: "devicon-django-plain tech-icon" },
{ name: "React", icon: "devicon-react-original tech-icon" },
{ name: "Pandas", icon: "devicon-pandas-original tech-icon" }
],
links: {
github: "https://github.com/SecCodeSmith/dataforge",
demo: "https://dataforge.demo.com"
}
}, },
{
id: "visionkeeper",
title: "VisionKeeper: Object Recognition System",
description: "A real-time object recognition system optimized for edge devices. Capable of identifying and tracking objects with high accuracy even in challenging lighting conditions.",
image: "/images/projects/visionkeeper.jpg",
category: ["ml"],
technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "PyTorch", icon: "devicon-pytorch-original tech-icon" },
{ name: "OpenCV", icon: "fas fa-camera tech-icon" },
{ name: "NVIDIA Jetson", icon: "fas fa-microchip tech-icon" }
],
links: {
github: "https://github.com/SecCodeSmith/visionkeeper"
} }
}
]; type RawProject = Omit<ProjectProps, 'category'> & { category: string[] };
const rawProjects: RawProject[] = await res.json();
// map the category-keys back to your CategoryMap objects
export const projectsData: ProjectProps[] = rawProjects.map(p => ({
...p,
category: p.category.map(key => {
const cat = Categories[key];
if (!cat) throw new Error(`Unknown project category key: ${key}`);
return cat;
})
}));
+102
View File
@@ -0,0 +1,102 @@
export const randomCodeLineData :string[] = [
'print("Hello, world!")',
'int main() { return 0; }',
'std::vector<int> v{1, 2, 3, 4};',
'Console.WriteLine(\"Enter your name:\");',
'for i in range(10):',
'char* buffer = malloc(256);',
'using (var fs = new FileStream(path, FileMode.Open))',
'if (x > y) x = y;',
'def factorial(n):',
'double result = pow(x, y);',
'string greeting = \"Hi there\";',
'auto it = map.find(key);',
'List<string> names = new List<string>();',
'while (ptr != nullptr) {',
'with open(\"file.txt\", \"r\") as f:',
'int *arr = new int[10];',
'var dict = new Dictionary<string, int>();',
'std::cout << \"Value: \" << value << std::endl;',
'await Task.Delay(1000);',
'lambda x: x * x',
'float pi = 3.14159f;',
'bool success = TryParse(input, out result);',
'try { /* ... */ } catch (Exception ex) { }',
'def hello(name=\"User\"):',
'constexpr int MAX = 100;',
'public class Person { }',
'if __name__ == \"__main__\":',
'switch(choice) { case 1: break; }',
'var json = JsonConvert.DeserializeObject(data);',
'std::sort(v.begin(), v.end());',
'Console.ReadKey();',
'for (int i = 0; i < n; i++)',
'x = x[::-1]',
'char c = getchar();',
'[Obsolete(\"Use NewMethod instead\")]',
'str_list = [s.upper() for s in strings]',
'uint64_t id = generate_id();',
'public interface IService { }',
'import os',
'static void Helper() { }',
'client.SendAsync(request);',
'matrix[i][j] = i * j;',
'def merge(a, b): return a + b',
'std::unique_ptr<Foo> p = std::make_unique<Foo>();',
'Task.Run(() => DoWork());',
'with lock(mutex):',
'vector.push_back(elem);',
'string.Join(\", \", list)',
'if (obj is MyClass mc) { }',
'os.remove(\"temp.txt\")',
'char16_t ch = u\'A\';',
'public static readonly string ID = Guid.NewGuid().ToString();',
'def fib(n): return fib(n-1) + fib(n-2)',
'memcpy(dest, src, size);',
'foreach (var item in collection) { }',
'assert(x != null);',
'std::thread t([](){ doWork(); });',
'[Serializable]',
'lst.append(item)',
'GC.Collect();',
'auto lambda = [](int x){ return x+1; };',
'public override string ToString() => Name;',
'if (!File.Exists(path)) File.Create(path);',
'double average = accumulate(v.begin(), v.end(), 0.0) / v.size();',
'using System.Text;',
'def decorator(func):',
'for (auto &p : pairs) { }',
'var query = from x in db.Items select x;',
'bytes_data = b\"\\x00\\xFF\"',
'cout << std::hex << n;',
'string.Format(\"{0}, {1}\", a, b);',
'import sys, json',
'bool isEmpty = list.Count == 0;',
'std::map<string,int> mp;',
'Console.ForegroundColor = ConsoleColor.Green;',
'def prime(n):',
'int score = Random().Next(0, 100);',
'var path = Path.Combine(dir, file);',
'auto now = std::chrono::system_clock::now();',
'if (val?.Length > 0) { }',
'with session.begin():',
'list<int> primes;',
'public event EventHandler Clicked;',
'x, y = y, x',
'char16_t* wstr = L\"wide\";',
'dynamic obj = new ExpandoObject();',
'assert(sizeof(int) == 4);',
'Console.Write($\"Count: {count}\");',
'std::tie(a, b) = getPair();',
'byte[] buffer = new byte[1024];',
'def async_func():',
'virtual void Render() override;',
'os.getcwd()',
'for k, v in dict.items():',
'std::shared_ptr<Foo> sp = ptr;',
'public bool Equals(Person other) => Id==other.Id;',
'return x is not None',
'size_t len = strlen(str);',
'var enumerable = Enumerable.Range(1,10);',
'float angle = atan2(y, x);',
]
+7
View File
@@ -0,0 +1,7 @@
import {type SkillCardProps} from '../untils/SkillCardProps';
const url = `${import.meta.env.BASE_URL}data/SkillCardProps.json`;
const res = await fetch(url);
if (!res.ok) throw new Error(`Failed to fetch skill card data: ${res.status}`);
export const skillCardData: SkillCardProps[] = await res.json();
+7
View File
@@ -0,0 +1,7 @@
import {type SocialLink} from "../untils/SocialLink";
const url = `${import.meta.env.BASE_URL}data/SocialLink.json`;
const res = await fetch(url);
if (!res.ok) throw new Error(`Failed to fetch skill card data: ${res.status}`);
export const socialLinkData: SocialLink[] = await res.json();
+1 -1
View File
@@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import App from './App.tsx' import App from './App.tsx'
import './index.css' import '@styles/index.scss'
ReactDOM.createRoot(document.getElementById('root')!).render( ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode> <React.StrictMode>
View File
+63 -265
View File
@@ -1,6 +1,10 @@
import { PageHeader } from '../components/PageHeader'; import { PageHeader } from '../components/PageHeader';
import { aboutProps } from '../data/AboutProps';
import style from '@styles/About.module.scss';
export const About = () => { export const About = () => {
const data = aboutProps;
return ( return (
<> <>
<PageHeader <PageHeader
@@ -13,36 +17,36 @@ export const About = () => {
<div className="container"> <div className="container">
<div className="row"> <div className="row">
<div className="col-lg-4"> <div className="col-lg-4">
<h2 className="section-title">The Master Behind the Mask</h2> <h2 className={`${style.sectionTitle}`}>The Master Behind the Mask</h2>
<div className="profile-image"> <div className={style.profileImage}>
<img src="/images/profile.jpg" alt="SecCodeSmith" /> <img src="/images/profile.jpg" alt="SecCodeSmith" />
</div> </div>
</div> </div>
<div className="col-lg-8"> <div className="col-lg-8">
<div className="intro-text"> <div className={style.introText}>
<p>Greetings, seeker of digital arcana. I am <strong>SecCodeSmith</strong>, a master craftsman of code and conductor of silicon. For over a decade, I have been forging robust solutions across the realms of embedded systems, machine learning, and web development.</p> <p className={style.introText}>Greetings, seeker of digital arcana. I am <strong>SecCodeSmith</strong>, a master craftsman of code and conductor of silicon. For over a decade, I have been forging robust solutions across the realms of embedded systems, machine learning, and web development.</p>
<p>My journey began in the depths of <span className="accent">low-level programming</span>, where I learned to bend hardware to my will through carefully crafted instructions. From the arcane energies of microcontrollers to the vast landscapes of cloud architecture, I have honed my skills to create solutions that stand the test of time.</p> <p className={style.introText}>My journey began in the depths of <span className={style.accent}>low-level programming</span>, where I learned to bend hardware to my will through carefully crafted instructions. From the arcane energies of microcontrollers to the vast landscapes of cloud architecture, I have honed my skills to create solutions that stand the test of time.</p>
<p>What distinguishes my work is the <strong>meticulous attention to security</strong> and performance. Like a blacksmith who understands that a sword's true value lies not just in its edge but in the integrity of its steel, I craft code that is not merely functional but resilient against the chaotic forces of the digital wilderness.</p> <p className={style.introText}>What distinguishes my work is the <strong>meticulous attention to security</strong> and performance. Like a blacksmith who understands that a sword's true value lies not just in its edge but in the integrity of its steel, I craft code that is not merely functional but resilient against the chaotic forces of the digital wilderness.</p>
<p>Beyond the technical realms, I am a devoted practitioner of open-source sorcery, contributing to projects that empower others to create and innovate. When not at the forge, I can be found exploring the ancient texts of computer science, delving into new programming languages, or mentoring apprentices on their own journey.</p> <p className={style.introText}>Beyond the technical realms, I am a devoted practitioner of open-source sorcery, contributing to projects that empower others to create and innovate. When not at the forge, I can be found exploring the ancient texts of computer science, delving into new programming languages, or mentoring apprentices on their own journey.</p>
<div className="mt-4"> <div className="mt-4">
<a href="#" className="connect-link"> <a href="#" className={style.connectLink}>
<i className="fab fa-github connect-icon"></i> <i className={`fab fa-github ${style.connectIcon}`}></i>
<span>GitHub</span> <span>GitHub</span>
</a> </a>
<a href="#" className="connect-link"> <a href="#" className={style.connectLink}>
<i className="fab fa-linkedin connect-icon"></i> <i className={`fab fa-linkedin ${style.connectIcon}`}></i>
<span>LinkedIn</span> <span>LinkedIn</span>
</a> </a>
<a href="#" className="connect-link"> <a href="#" className={style.connectLink}>
<i className="fab fa-twitter connect-icon"></i> <i className={`fab fa-twitter ${style.connectIcon}`}></i>
<span>Twitter</span> <span>Twitter</span>
</a> </a>
<a href="#" className="connect-link"> <a href="#" className={style.connectLink}>
<i className="fas fa-envelope connect-icon"></i> <i className={`fas fa-envelope ${style.connectIcon}`}></i>
<span>Email</span> <span>Email</span>
</a> </a>
</div> </div>
@@ -57,49 +61,22 @@ export const About = () => {
<div className="container"> <div className="container">
<div className="row mb-4"> <div className="row mb-4">
<div className="col-12"> <div className="col-12">
<h2 className="section-title">Forging Principles</h2> <h2 className={` ${style.sectionTitle}`}>Forging Principles</h2>
</div> </div>
</div> </div>
<div className="row g-4"> <div className={`row g-4 ${style.row}`}>
<div className="col-md-6 col-lg-3"> {data.coreValues.map((value, index) => (
<div className="value-card h-100"> <div className="col-md-6 col-lg-3" key={index}>
<div className="value-icon"> <div className={` h-100 ${style.valueCard}`}>
<i className="fas fa-shield-alt"></i> <div className={style.valueIcon}>
<i className={value.icon}></i>
</div> </div>
<h3 className="value-title">Security by Design</h3> <h3 className={`${style.valueTitle}`}>{value.title}</h3>
<p className="value-text">Security is not an afterthought but the very foundation upon which robust systems are built. Every line of code is crafted with defensive principles in mind.</p> <p className={`${style.valueText}`}>{value.description}</p>
</div> </div>
</div> </div>
))}
<div className="col-md-6 col-lg-3">
<div className="value-card h-100">
<div className="value-icon">
<i className="fas fa-tachometer-alt"></i>
</div>
<h3 className="value-title">Performance Optimization</h3>
<p className="value-text">Like a finely-honed blade, code should be both effective and efficient. I meticulously optimize every component for maximum speed and minimal resource consumption.</p>
</div>
</div>
<div className="col-md-6 col-lg-3">
<div className="value-card h-100">
<div className="value-icon">
<i className="fas fa-gem"></i>
</div>
<h3 className="value-title">Elegant Simplicity</h3>
<p className="value-text">True mastery is revealed not in complexity but in elegant simplicity. I strive to create solutions that are accessible, maintainable, and beautifully crafted.</p>
</div>
</div>
<div className="col-md-6 col-lg-3">
<div className="value-card h-100">
<div className="value-icon">
<i className="fas fa-sync-alt"></i>
</div>
<h3 className="value-title">Continuous Evolution</h3>
<p className="value-text">The digital landscape changes with the seasons. I embrace continuous learning and adaptation, integrating new techniques and technologies to forge ever-better solutions.</p>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@@ -109,141 +86,31 @@ export const About = () => {
<div className="container"> <div className="container">
<div className="row mb-4"> <div className="row mb-4">
<div className="col-12"> <div className="col-12">
<h2 className="section-title">Arsenal of Expertise</h2> <h2 className={`${style.sectionTitle}`}>Arsenal of Expertise</h2>
</div> </div>
</div> </div>
<div className="row g-4"> <div className="row g-4">
<div className="col-md-6 col-lg-3"> {data.technicalArsenal.map((value, index) => (
<div className="skill-card h-100"> <div className="col-md-6 col-lg-3" key={index}>
<div className="skill-header"> <div className={`h-100 ${style.skillCard}`} key={index}>
<div className="skill-icon"> <div className={`${style.skillHeader}`}>
<i className="fas fa-microchip"></i> <div className={`${style.skillIcon}`}>
<i className={value.icon}></i>
</div> </div>
<h3 className="skill-title">Embedded Systems</h3> <h3 className={`skill-title ${style.skillTitle}`}>{value.title}</h3>
</div> </div>
<ul className="list-unstyled"> <ul className="list-unstyled">
<li className="mb-2"> {value.skills.map((skillItem, skillIndex) => (
<i className="fas fa-check skill-check"></i> <li className="mb-2" key={skillIndex}>
<span>STM32 microcontroller programming (I2C, SPI, UART)</span> <i className={`fas fa-check skill-check ${style.skillCheck}`}></i>
</li> <span>{skillItem}</span>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>ESP32 wireless solutions</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Firmware development with C/C++</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Raspberry Pi and Pico-SDK integration</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>RTOS implementation</span>
</li> </li>
))}
</ul> </ul>
</div> </div>
</div> </div>
))}
<div className="col-md-6 col-lg-3">
<div className="skill-card h-100">
<div className="skill-header">
<div className="skill-icon">
<i className="fas fa-brain"></i>
</div>
<h3 className="skill-title">AI & Data Analysis</h3>
</div>
<ul className="list-unstyled">
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Machine learning with PyTorch</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Data manipulation with Pandas</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Predictive modeling via Scikit-learn</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Computer vision with Scikit-image</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Edge AI deployment</span>
</li>
</ul>
</div>
</div>
<div className="col-md-6 col-lg-3">
<div className="skill-card h-100">
<div className="skill-header">
<div className="skill-icon">
<i className="fas fa-window-maximize"></i>
</div>
<h3 className="skill-title">Web Development</h3>
</div>
<ul className="list-unstyled">
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Backend development with ASP.NET</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Database management with Entity Framework</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Frontend frameworks: Blazor, React</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Responsive design with Bootstrap</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Python-based web solutions with Django</span>
</li>
</ul>
</div>
</div>
<div className="col-md-6 col-lg-3">
<div className="skill-card h-100">
<div className="skill-header">
<div className="skill-icon">
<i className="fas fa-server"></i>
</div>
<h3 className="skill-title">DevOps & Infrastructure</h3>
</div>
<ul className="list-unstyled">
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Containerization with Docker</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Multi-container orchestration via Docker-compose</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Linux server administration</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>CI/CD pipeline implementation</span>
</li>
<li className="mb-2">
<i className="fas fa-check skill-check"></i>
<span>Build system configuration with CMake</span>
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</section > </section >
@@ -253,61 +120,22 @@ export const About = () => {
<div className="container"> <div className="container">
<div className="row mb-5"> <div className="row mb-5">
<div className="col-12 text-center"> <div className="col-12 text-center">
<h2 className="section-title">The Smith's Journey</h2> <h2 className={`section-title ${style.sectionTitle}`}>The Smith's Journey</h2>
</div> </div>
</div> </div>
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<ul className="timeline"> <ul className={`${style.timeline}`}>
{/* Timeline Item 1 */} {data.professionalJourney.map((journey, index) => (
<li className="timeline-item clearfix"> <li className={`${style.timelineItem} clearfix`} key={index}>
<div className="timeline-dot"></div> <div className={`${style.timelineDot}`}></div>
<div className="timeline-content"> <div className={`${style.timelineContent}`}>
<div className="timeline-date">2021 - Present</div> <div className={`${style.timelineDate}`}>{journey.duration}</div>
<h3 className="timeline-title">Lead Systems Engineer</h3> <h3 className={`${style.timelineTitle}`}>{journey.title}</h3>
<p className="timeline-text">Architecting secure IoT solutions for critical infrastructure, combining embedded firmware expertise with cloud integration. Leading a team of 8 engineers across hardware and software disciplines.</p> <p className={`${style.timelineText}`}>{journey.description}</p>
</div>
</li>
{/* Timeline Item 2 */}
<li className="timeline-item clearfix">
<div className="timeline-dot"></div>
<div className="timeline-content">
<div className="timeline-date">2018 - 2021</div>
<h3 className="timeline-title">Senior Embedded Developer</h3>
<p className="timeline-text">Developed firmware for next-generation industrial control systems. Implemented machine learning capabilities on resource-constrained devices, reducing predictive maintenance false positives by 87%.</p>
</div>
</li>
{/* Timeline Item 3 */}
<li className="timeline-item clearfix">
<div className="timeline-dot"></div>
<div className="timeline-content">
<div className="timeline-date">2015 - 2018</div>
<h3 className="timeline-title">Full-Stack Developer</h3>
<p className="timeline-text">Created web applications and APIs for data visualization and analysis. Specialized in integrating hardware systems with web interfaces, enabling real-time monitoring of distributed sensor networks.</p>
</div>
</li>
{/* Timeline Item 4 */}
<li className="timeline-item clearfix">
<div className="timeline-dot"></div>
<div className="timeline-content">
<div className="timeline-date">2012 - 2015</div>
<h3 className="timeline-title">Firmware Engineer</h3>
<p className="timeline-text">Designed and implemented firmware for medical devices, focusing on reliability and safety-critical operations. Reduced power consumption by 35% while improving processing performance.</p>
</div>
</li>
{/* Timeline Item 5 */}
<li className="timeline-item clearfix">
<div className="timeline-dot"></div>
<div className="timeline-content">
<div className="timeline-date">2010 - 2012</div>
<h3 className="timeline-title">Junior Software Developer</h3>
<p className="timeline-text">Began the journey into the digital forge, working on embedded C programming for consumer electronics. Learned the fundamentals of hardware-software integration and real-time systems.</p>
</div> </div>
</li> </li>
))}
</ul> </ul>
</div> </div>
</div> </div>
@@ -315,66 +143,36 @@ export const About = () => {
</section > </section >
{/* Testimonials Section */} {/* Testimonials Section */}
{data.testimonials.length > 0 && (
< section className="py-5" > < section className="py-5" >
<div className="container"> <div className="container">
<div className="row mb-4"> <div className="row mb-4">
<div className="col-12"> <div className="col-12">
<h2 className="section-title">Tales from the Guild</h2> <h2 className={`section-title ${style.sectionTitle}`}>Tales from the Guild</h2>
</div> </div>
</div> </div>
<div className="row"> <div className="row">
<div className="col-lg-8 mx-auto"> <div className="col-lg-8 mx-auto">
{/* Testimonial 1 */} {data.testimonials.map((testimonial, index) => (
<div className="testimonial"> <div className="testimonial" key={index}>
<div className="testimonial-content"> <div className="testimonial-content">
"Working with SecCodeSmith transformed our approach to IoT security. Their expertise in both embedded systems and network security allowed us to create a solution that our clients trust implicitly. The attention to detail and foresight regarding potential vulnerabilities saved us from what could have been catastrophic security incidents." {testimonial.content}
</div> </div>
<div className="d-flex align-items-center gap-3 testimonial-author"> <div className="d-flex align-items-center gap-3 testimonial-author">
<div className="author-avatar"> <div className="author-avatar">
<img src="/images/testimonials/alexandra.jpg" alt="Alexandra Foster" /> <img src="/images/testimonials/james.jpg" alt={testimonial.author} />
</div> </div>
<div className="author-info"> <div className="author-info">
<h4 className="author-name">Alexandra Foster</h4> <h4 className="author-name">{testimonial.author}</h4>
<div className="author-title">CTO, ConnectedWorld Technologies</div> <div className="author-title">{testimonial.position}</div>
</div> </div>
</div> </div>
</div> </div>
))}
{/* Testimonial 2 */}
<div className="testimonial">
<div className="testimonial-content">
"The custom firmware developed by SecCodeSmith for our industrial controllers exceeded all expectations. Not only was the code incredibly efficient, but the documentation was meticulous—something rare in our industry. Three years later, we're still building upon that solid foundation with minimal issues."
</div>
<div className="d-flex align-items-center gap-3 testimonial-author">
<div className="author-avatar">
<img src="/images/testimonials/marcus.jpg" alt="Marcus Chen" />
</div>
<div className="author-info">
<h4 className="author-name">Marcus Chen</h4>
<div className="author-title">Lead Engineer, Precision Automation Inc.</div>
</div> </div>
</div> </div>
</div> </div>
</section >)}
{/* Testimonial 3 */}
<div className="testimonial">
<div className="testimonial-content">
"I've collaborated with numerous developers over my 20-year career, but SecCodeSmith stands apart for their ability to bridge the gap between theoretical concepts and practical implementation. Their machine learning models for anomaly detection operate efficiently even on our limited hardware, which competitors claimed was impossible."
</div>
<div className="d-flex align-items-center gap-3 testimonial-author">
<div className="author-avatar">
<img src="/images/testimonials/sophia.jpg" alt="Sophia Rodriguez" />
</div>
<div className="author-info">
<h4 className="author-name">Sophia Rodriguez</h4>
<div className="author-title">Research Director, Advanced Sensing Lab</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</> </>
); );
}; };
-342
View File
@@ -1,342 +0,0 @@
/* Page Header */
.page-header {
padding-top: 7rem;
padding-bottom: 3rem;
background-color: var(--secondary-dark);
position: relative;
overflow: hidden;
}
.page-header::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: url('/api/placeholder/1200/400');
opacity: 0.15;
filter: brightness(0.3);
z-index: 0;
}
.page-title {
font-size: 3rem;
margin-bottom: 1rem;
position: relative;
display: inline-block;
}
.page-title::after {
content: "";
position: absolute;
width: 60px;
height: 3px;
background: var(--accent-fire);
bottom: -10px;
left: 50%;
transform: translateX(-50%);
box-shadow: var(--glow-effect);
}
.page-subtitle {
font-size: 1.2rem;
color: var(--text-ash);
}
/* Card styling */
.card {
background-color: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: 8px;
transition: all 0.3s ease;
margin-bottom: 1.5rem;
overflow: hidden;
}
.card:hover {
transform: translateY(-5px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.card-img-top {
height: 200px;
object-fit: cover;
}
.featured-img {
height: 300px;
}
.card-title {
font-size: 1.5rem;
margin-bottom: 0.75rem;
}
.card-title a {
color: var(--text-light);
text-decoration: none;
transition: color 0.3s;
}
.card-title a:hover {
color: var(--accent-fire);
}
.card-text {
color: var(--text-ash);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
}
.featured-card .card-title {
font-size: 1.8rem;
}
.card-footer {
background-color: rgba(0, 0, 0, 0.2);
border-top: 1px solid var(--card-border);
}
/* Category badges */
.category-badge {
position: absolute;
top: 1rem;
right: 1rem;
background-color: rgba(139, 0, 0, 0.9);
color: var(--text-light);
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
}
.featured-badge {
position: absolute;
top: 1rem;
left: 1rem;
background: linear-gradient(45deg, var(--accent-dragon), var(--accent-fire));
color: var(--text-light);
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
display: flex;
align-items: center;
gap: 5px;
}
/* Meta items */
.meta-item {
display: inline-flex;
align-items: center;
gap: 6px;
margin-right: 1rem;
color: var(--text-ash);
font-size: 0.9rem;
}
.meta-icon {
color: var(--accent-fire);
}
/* Read more link */
.read-more {
display: inline-block;
color: var(--accent-fire);
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
position: relative;
}
.read-more::after {
content: "→";
margin-left: 5px;
transition: transform 0.3s ease;
}
.read-more:hover::after {
transform: translateX(5px);
}
/* Tags */
.post-tag {
display: inline-block;
background-color: rgba(255, 100, 0, 0.1);
color: var(--accent-fire);
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 500;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
}
.post-tag:hover {
background-color: rgba(255, 100, 0, 0.2);
transform: translateY(-2px);
}
/* Sidebar */
.sidebar-widget {
background-color: var(--card-bg);
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
border: 1px solid var(--card-border);
}
.widget-title {
font-size: 1.2rem;
margin-bottom: 1.25rem;
padding-bottom: 0.625rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
position: relative;
}
.widget-title::after {
content: "";
position: absolute;
width: 40px;
height: 2px;
background: var(--accent-fire);
bottom: -1px;
left: 0;
}
.search-form .form-control {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid var(--card-border);
color: var(--text-light);
}
.search-form .form-control:focus {
box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25);
border-color: var(--accent-fire);
}
.search-form .btn {
background-color: var(--accent-fire);
border-color: var(--accent-fire);
}
.search-form .btn:hover {
background-color: var(--accent-dragon);
border-color: var(--accent-dragon);
}
/* Lists in sidebar */
.list-group-item {
background-color: transparent;
border-color: rgba(255, 255, 255, 0.05);
color: var(--text-ash);
transition: all 0.3s ease;
}
.list-group-item:hover {
background-color: rgba(255, 100, 0, 0.1);
}
.list-group-item a {
color: var(--text-ash);
text-decoration: none;
display: flex;
justify-content: space-between;
align-items: center;
}
.list-group-item a:hover {
color: var(--accent-fire);
}
.category-count {
background-color: rgba(255, 100, 0, 0.1);
color: var(--accent-fire);
padding: 2px 8px;
border-radius: 10px;
font-size: 0.8rem;
}
/* Recent posts in sidebar */
.recent-post-item {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.recent-post-item:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.recent-post-thumb {
width: 60px;
height: 60px;
border-radius: 4px;
object-fit: cover;
}
.recent-post-info {
flex-grow: 1;
}
.recent-post-title {
font-size: 0.9rem;
line-height: 1.4;
margin-bottom: 5px;
}
.recent-post-title a {
color: var(--text-ash);
text-decoration: none;
transition: color 0.3s;
}
.recent-post-title a:hover {
color: var(--accent-fire);
}
.recent-post-date {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.5);
}
/* Pagination */
.page-item .page-link {
background-color: var(--card-bg);
border-color: var(--card-border);
color: var(--text-ash);
}
.page-item .page-link:hover {
background-color: rgba(255, 100, 0, 0.1);
color: var(--accent-fire);
border-color: var(--accent-fire);
}
.page-item.active .page-link {
background-color: var(--accent-fire);
border-color: var(--accent-fire);
color: var(--text-light);
}
/* Binary background effect */
.binary-line {
position: absolute;
font-family: 'Fira Code', monospace;
color: var(--accent-fire);
font-size: 0.8rem;
opacity: 0.15;
pointer-events: none;
}
+35 -45
View File
@@ -3,12 +3,12 @@ import { PageHeader } from '../components/PageHeader';
import { BlogCard } from '../components/BlogCard'; import { BlogCard } from '../components/BlogCard';
import { blogPostsData } from '../data/blogPostsData'; import { blogPostsData } from '../data/blogPostsData';
import './Blog.module.css'; import style from '@styles/Blog.module.scss';
export const Blog = () => { export const Blog = () => {
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [currentPage, /*setCurrentPage*/] = useState(1);
// Filter blog posts based on search query
const filteredPosts = searchQuery const filteredPosts = searchQuery
? blogPostsData.filter(post => ? blogPostsData.filter(post =>
post.title.toLowerCase().includes(searchQuery.toLowerCase()) || post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
@@ -17,21 +17,18 @@ export const Blog = () => {
) )
: blogPostsData; : blogPostsData;
// Get the featured post const filteredPostPageCount = Math.ceil(filteredPosts.length / 6);
const featuredPost = blogPostsData.find(post => post.featured); const featuredPost = blogPostsData.find(post => post.featured);
// Get regular posts (non-featured)
const regularPosts = filteredPosts.filter(post => !post.featured); const regularPosts = filteredPosts.filter(post => !post.featured);
// Handle search input change
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(e.target.value); setSearchQuery(e.target.value);
}; };
// Generate a list of unique categories
const categories = Array.from(new Set(blogPostsData.map(post => post.category))); const categories = Array.from(new Set(blogPostsData.map(post => post.category)));
// Generate a list of unique tags
const allTags = blogPostsData.flatMap(post => post.tags); const allTags = blogPostsData.flatMap(post => post.tags);
const uniqueTags = Array.from(new Set(allTags)); const uniqueTags = Array.from(new Set(allTags));
@@ -44,9 +41,7 @@ export const Blog = () => {
<div className="container mt-5"> <div className="container mt-5">
<div className="row"> <div className="row">
{/* Main Content */}
<div className="col-lg-8"> <div className="col-lg-8">
{/* Featured Post */}
{featuredPost && (!searchQuery || {featuredPost && (!searchQuery ||
featuredPost.title.toLowerCase().includes(searchQuery.toLowerCase()) || featuredPost.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
featuredPost.excerpt.toLowerCase().includes(searchQuery.toLowerCase()) || featuredPost.excerpt.toLowerCase().includes(searchQuery.toLowerCase()) ||
@@ -55,7 +50,6 @@ export const Blog = () => {
<BlogCard post={featuredPost} /> <BlogCard post={featuredPost} />
)} )}
{/* Regular Posts */}
<div className="row"> <div className="row">
{regularPosts.map(post => ( {regularPosts.map(post => (
<div className="col-md-6" key={post.id}> <div className="col-md-6" key={post.id}>
@@ -64,7 +58,6 @@ export const Blog = () => {
))} ))}
</div> </div>
{/* No Posts Message */}
{filteredPosts.length === 0 && ( {filteredPosts.length === 0 && (
<div className="text-center mt-5"> <div className="text-center mt-5">
<h3>No posts found matching your search.</h3> <h3>No posts found matching your search.</h3>
@@ -77,34 +70,34 @@ export const Blog = () => {
</div> </div>
)} )}
{/* Pagination */} {filteredPostPageCount > 1 && (
{filteredPosts.length > 0 && (
<nav className="mt-5" aria-label="Blog pagination"> <nav className="mt-5" aria-label="Blog pagination">
<ul className="pagination justify-content-center"> <ul className="pagination justify-content-center">
<li className="page-item"> {(currentPage > 1) && ( <li className={`page-item ${style.pageItem}`}>
<a className="page-link" href="#" aria-label="Previous"> <a className={`page-link ${style.pageLink}`} href="#" aria-label="Previous">
<i className="fas fa-angle-left"></i> <i className="fas fa-angle-left"></i>
</a> </a>
</li>)}
{Array.from({ length: filteredPostPageCount }, (_, index) => (
<li className={`page-item ${style.pageItem}`} key={index}>
<a className={`page-link ${style.pageLink}`} href="#">{index + 1}</a>
</li> </li>
<li className="page-item active"><a className="page-link" href="#">1</a></li> ))}
<li className="page-item"><a className="page-link" href="#">2</a></li> {(currentPage < filteredPostPageCount) && ( <li className={`page-item ${style.pageItem}`}>
<li className="page-item"><a className="page-link" href="#">3</a></li> <a className={`page-link ${style.pageLink}`} href="#" aria-label="Next">
<li className="page-item">
<a className="page-link" href="#" aria-label="Next">
<i className="fas fa-angle-right"></i> <i className="fas fa-angle-right"></i>
</a> </a>
</li> </li>)}
</ul> </ul>
</nav> </nav>
)} )}
</div> </div>
{/* Sidebar */} <div className={`col-lg-4 sidebar`}>
<div className="col-lg-4 sidebar"> <div className={`sidebar-widget ${style.sidebarWidget}`}>
{/* Search Widget */} <h3 className={`widget-title ${style.widgetTitle}`}>Search</h3>
<div className="sidebar-widget"> <form className={`search-form ${style.searchForm}`}>
<h3 className="widget-title">Search</h3>
<form className="search-form">
<div className="input-group"> <div className="input-group">
<input <input
type="text" type="text"
@@ -113,24 +106,23 @@ export const Blog = () => {
value={searchQuery} value={searchQuery}
onChange={handleSearchChange} onChange={handleSearchChange}
/> />
<button className="btn btn-primary" type="submit"> <button className={`btn btn-primary ${style.btn}`} type="submit">
<i className="fas fa-search"></i> <i className="fas fa-search"></i>
</button> </button>
</div> </div>
</form> </form>
</div> </div>
{/* Categories Widget */} <div className={`sidebar-widget ${style.sidebarWidget}`}>
<div className="sidebar-widget"> <h3 className={`widget-title ${style.widgetTitle}`}>Categories</h3>
<h3 className="widget-title">Categories</h3>
<div className="list-group"> <div className="list-group">
{categories.map((category, index) => { {categories.map((category, index) => {
const count = blogPostsData.filter(post => post.category === category).length; const count = blogPostsData.filter(post => post.category === category).length;
return ( return (
<div className="list-group-item" key={index}> <div className={`list-group-item ${style.listGroupItem}`} key={index}>
<a href="#" onClick={() => setSearchQuery(category)}> <a href="#" onClick={() => setSearchQuery(category)}>
<span>{category}</span> <span>{category}</span>
<span className="category-count">{count}</span> <span className={`category-count ${style.categoryCount}`}>{count}</span>
</a> </a>
</div> </div>
); );
@@ -138,32 +130,30 @@ export const Blog = () => {
</div> </div>
</div> </div>
{/* Recent Posts Widget */} <div className={`sidebar-widget ${style.sidebarWidget}`}>
<div className="sidebar-widget"> <h3 className={`widget-title ${style.widgetTitle}`}>Recent Posts</h3>
<h3 className="widget-title">Recent Posts</h3>
<div className="recent-posts"> <div className="recent-posts">
{blogPostsData.slice(0, 3).map(post => ( {blogPostsData.slice(0, 3).map(post => (
<div className="recent-post-item" key={post.id}> <div className={`recent-post-item ${style.recentPostItem}`} key={post.id}>
<img src={post.image} className="recent-post-thumb" alt={post.title} /> <img src={post.image} className={`recent-post-thumb ${style.recentPostThumb}`} alt={post.title} />
<div className="recent-post-info"> <div className={style.recentPostInfo}>
<h4 className="recent-post-title"> <h4 className={style.recentPostTitle}>
<a href={`/blog/${post.slug}`}>{post.title}</a> <a href={`/blog/${post.slug}`}>{post.title}</a>
</h4> </h4>
<span className="recent-post-date">{post.date}</span> <span className={style.recentPostDate}>{post.date}</span>
</div> </div>
</div> </div>
))} ))}
</div> </div>
</div> </div>
{/* Tags Widget */} <div className={`sidebar-widget ${style.sidebarWidget}`}>
<div className="sidebar-widget"> <h3 className={`widget-title ${style.widgetTitle}`}>Popular Tags</h3>
<h3 className="widget-title">Popular Tags</h3>
<div> <div>
{uniqueTags.map((tag, index) => ( {uniqueTags.map((tag, index) => (
<span <span
key={index} key={index}
className="post-tag" className={style.postTag}
onClick={() => setSearchQuery(tag)} onClick={() => setSearchQuery(tag)}
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
> >
View File
+194 -104
View File
@@ -3,22 +3,62 @@ import { useParams, Link } from 'react-router-dom';
import { blogPostsData } from '../data/blogPostsData'; import { blogPostsData } from '../data/blogPostsData';
import { NotFound } from './NotFound'; import { NotFound } from './NotFound';
import './BlogPost.module.css';
import style from '@styles/BlogPost.module.scss';
interface TableOfContentsItem {
title: string;
id: number;
children?: TableOfContentsItem[];
}
function dedent(str: string): string {
const lines = str.split('\n');
while (lines.length > 0 && lines[0].trim() === '') {
lines.shift();
}
while (lines.length > 0 && lines[lines.length - 1].trim() === '') {
lines.pop();
}
const indents = lines
.filter(line => line.trim().length > 0)
.map(line => line.match(/^ */)![0].length);
const minIndent = indents.length ? Math.min(...indents) : 0;
return lines.map(line => line.slice(minIndent)).join('\n');
}
const TableOfContents = ({ toc }: { toc: TableOfContentsItem[] }) => {
return (
<ul className={style.tocList}>
{toc.map(item => (
<li key={item.id} className={style.tocItem}>
<a href={`#${item.id}`} className={style.tocLink}>{item.title}</a>
{item.children && item.children.length > 0 && (
<TableOfContents toc={item.children} />
)}
</li>
))}
</ul>
);
}
export const BlogPost = () => { export const BlogPost = () => {
const { slug } = useParams<{ slug: string }>(); const { slug } = useParams<{ slug: string }>();
const [post, setPost] = useState(blogPostsData.find(post => post.slug === slug)); const [post, setPost] = useState(blogPostsData.find(post => post.slug === slug));
const [relatedPosts, setRelatedPosts] = useState(blogPostsData.slice(0, 3)); const [relatedPosts, setRelatedPosts] = useState(blogPostsData.slice(0, 3));
const [toc, setToc] = useState<TableOfContentsItem[]>([]);
useEffect(() => { useEffect(() => {
// Find the post by slug
const currentPost = blogPostsData.find(post => post.slug === slug); const currentPost = blogPostsData.find(post => post.slug === slug);
let id = 0;
const toc: TableOfContentsItem[] = [];
if (currentPost) { if (currentPost) {
// Set the post
setPost(currentPost); setPost(currentPost);
// Find related posts (same category or shared tags)
const related = blogPostsData const related = blogPostsData
.filter(p => p.id !== currentPost.id) .filter(p => p.id !== currentPost.id)
.filter(p => .filter(p =>
@@ -29,7 +69,85 @@ export const BlogPost = () => {
setRelatedPosts(related); setRelatedPosts(related);
// Scroll to top when new post is loaded let newContent = '';
if (currentPost.content) {
const content = dedent(currentPost.content);
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
if (/^#\s/.test(lines[i])) {
newContent += `<h2 id="${id}">${lines[i].replace(/^#+\s*/, '')}</h2> \n`;
toc.push({ title: lines[i].replace(/^#+\s*/, ''), id });
id++;
}
else if (/^##\s/.test(lines[i])) {
newContent += `<h3 id="${id}">${lines[i].replace(/^#+\s*/, '')}</h3> \n`;
if (toc.length > 0) {
const parent = toc[toc.length - 1];
if (!parent.children) {
parent.children = [];
}
parent.children.push({ title: lines[i].replace(/^#+\s*/, ''), id });
}
id++;
}
else if (/^###\s/.test(lines[i])) {
newContent += `<h4 id="${id}">${lines[i].replace(/^#+\s*/, '')}</h4> \n`;
if (toc.length > 0) {
const h2Parent = toc[toc.length - 1];
if (h2Parent.children && h2Parent.children.length > 0) {
const h3Parent = h2Parent.children[h2Parent.children.length - 1];
if (!h3Parent.children) {
h3Parent.children = [];
}
h3Parent.children.push({ title: lines[i].replace(/^#+\s*/, ''), id });
}
}
id++;
}
else if (/^\*\s/.test(lines[i])) {
newContent += '<ul>\n';
for (; i < lines.length; i++) {
if (/^\*\s/.test(lines[i])) {
newContent += `<li>${lines[i].replace(/^\*\s/, '')}</li>\n`;
}
else {
break;
}
}
newContent += '</ul>\n';
}
else if (/^\-\s/.test(lines[i])) {
newContent += '<ol>\n';
for (; i < lines.length; i++) {
if (/^\-\s/.test(lines[i])) {
newContent += `<li>${lines[i].replace(/^\-\s/, '')}</li>\n`;
}
else {
break;
}
}
newContent += '</ol>\n';
}
else {
newContent += `<p>${lines[i]}</p>\n`;
}
}
}
newContent = newContent.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
newContent = newContent.replace(/__(.*?)__/g, '<strong>$1</strong>');
newContent = newContent.replace(/\*(.*?)\*/g, '<em>$1</em>');
newContent = newContent.replace(/_(.*?)_/g, '<em>$1</em>');
newContent = newContent.replace(/`(.*?)`/g, '<code>$1</code>');
newContent = newContent.replace(/!\[(.*?)\]\((.*?)\)/g, '<img alt="$1" src="$2" />');
newContent = newContent.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2">$1</a>');
const contentElement = document.getElementById('postContent');
contentElement!.innerHTML = newContent;
setToc(toc);
window.scrollTo(0, 0); window.scrollTo(0, 0);
} }
}, [slug]); }, [slug]);
@@ -39,69 +157,69 @@ export const BlogPost = () => {
} }
return ( return (
<div className="post-container"> <div className={style.postContainer}>
<main className="post-main"> <main className={style.postMain}>
<article> <article>
{/* Post Header */} {/* Post Header */}
<div className="post-header"> <div className={style.postHeader}>
<div className="post-featured-image" style={{ backgroundImage: `url(${post.image})` }}></div> <div className={style.postFeaturedImage} style={{ backgroundImage: `url(${import.meta.env.BASE_URL}${post.image})` }}></div>
<div className="post-category">{post.category}</div> <div className={style.postCategory}>{post.category}</div>
</div> </div>
{/* Post Info */} {/* Post Info */}
<div className="post-info"> <div className={style.postInfo}>
<h1 className="post-title">{post.title}</h1> <h1 className={style.postTitle}>{post.title}</h1>
<div className="post-meta"> <div className={style.postMeta}>
<div className="meta-item"> <div className={style.metaItem}>
<i className="fas fa-calendar-alt meta-icon"></i> <i className={`fas fa-calendar-alt ${style.metaIcon}`}></i>
<span>{post.date}</span> <span>{post.date}</span>
</div> </div>
<div className="meta-item"> <div className={style.metaItem}>
<i className="fas fa-user meta-icon"></i> <i className={`fas fa-user ${style.metaIcon}`}></i>
<span>{post.author}</span> <span>{post.author}</span>
</div> </div>
<div className="meta-item"> <div className={style.metaItem}>
<i className="fas fa-clock meta-icon"></i> <i className={`fas fa-clock ${style.metaIcon}`}></i>
<span>{post.readTime}</span> <span>{post.readTime}</span>
</div> </div>
<div className="meta-item"> <div className={style.metaItem}>
<i className="fas fa-comments meta-icon"></i> <i className={`fas fa-comments ${style.metaIcon}`}></i>
<span>{post.commentCount} Comments</span> <span>{post.commentCount} Comments</span>
</div> </div>
</div> </div>
</div> </div>
{/* Post Content */} <div className={style.postContent} id="postContent">
<div className="post-content" dangerouslySetInnerHTML={{ __html: post.content }}></div> </div>
<div className={style.postContent}>
{/* Post Tags */} {/* Post Tags */}
<div className="post-tags"> <div className={style.postTags}>
{post.tags.map((tag, index) => ( {post.tags.map((tag, index) => (
<span key={index} className="post-tag">{tag}</span> <span key={index} className={style.postTag}>{tag}</span>
))} ))}
</div> </div>
{/* Share Section */} {/* Share Section */}
<div className="share-section"> <div className={style.shareSection}>
<h3 className="share-title">Share this arcane knowledge</h3> <h3 className={style.shareTitle}>Share this arcane knowledge</h3>
<div className="share-buttons"> <div className={style.shareButtons}>
<a href="#" className="share-button"><i className="fab fa-twitter"></i></a> <a href="#" className={style.shareButton}><i className="fab fa-twitter"></i></a>
<a href="#" className="share-button"><i className="fab fa-facebook-f"></i></a> <a href="#" className={style.shareButton}><i className="fab fa-facebook-f"></i></a>
<a href="#" className="share-button"><i className="fab fa-linkedin-in"></i></a> <a href="#" className={style.shareButton}><i className="fab fa-linkedin-in"></i></a>
<a href="#" className="share-button"><i className="fab fa-reddit-alien"></i></a> <a href="#" className={style.shareButton}><i className="fab fa-reddit-alien"></i></a>
<a href="#" className="share-button"><i className="far fa-envelope"></i></a> <a href="#" className={style.shareButton}><i className="far fa-envelope"></i></a>
</div> </div>
</div> </div>
{/* Author Info */} {/* Author Info */}
<div className="author-section"> <div className={style.authorSection}>
<div className="author-avatar"> <div className={style.authorAvatar}>
<img src="/images/author.jpg" alt="SecCodeSmith" /> <img src="/images/author.jpg" alt="SecCodeSmith" />
</div> </div>
<div className="author-info"> <div className={style.authorInfo}>
<h3 className="author-name">SecCodeSmith</h3> <h3 className={style.authorName}>SecCodeSmith</h3>
<p className="author-bio">Digital blacksmith forging embedded systems and IoT solutions in the fires of innovation. Specializes in low-level firmware, communication protocols, and hardware-software integration for resource-constrained devices.</p> <p className={style.authorBio}>Digital blacksmith forging embedded systems and IoT solutions in the fires of innovation. Specializes in low-level firmware, communication protocols, and hardware-software integration for resource-constrained devices.</p>
<div className="author-social"> <div className={style.authorSocial}>
<a href="#"><i className="fab fa-github"></i></a> <a href="#"><i className="fab fa-github"></i></a>
<a href="#"><i className="fab fa-twitter"></i></a> <a href="#"><i className="fab fa-twitter"></i></a>
<a href="#"><i className="fab fa-linkedin-in"></i></a> <a href="#"><i className="fab fa-linkedin-in"></i></a>
@@ -109,94 +227,66 @@ export const BlogPost = () => {
</div> </div>
</div> </div>
</div> </div>
{/*Temporarily disable comments section */}
{/* Comments Section */} { 0 && (
<div className="comments-section"> <div className={style.commentsSection}>
<h3 className="comments-title">Discussions ({post.commentCount})</h3> <h3 className={style.commentsTitle}>Discussions ({post.commentCount})</h3>
{/* Sample Comments */} {/* Sample Comments */}
<div className="comment"> <div className={style.comment}>
<div className="comment-avatar"> <div className={style.commentAvatar}>
<img src="/images/user-1.jpg" alt="User" /> <img src="/images/user-1.jpg" alt="User" />
</div> </div>
<div className="comment-content"> <div className={style.commentContent}>
<h4 className="comment-author">Michael Chen</h4> <h4 className={style.commentAuthor}>Michael Chen</h4>
<div className="comment-date">May 5, 2025 at 15:42</div> <div className={style.commentDate}>May 5, 2025 at 15:42</div>
<p className="comment-text">Excellent article! I've been struggling with I2C bus lockups in my weather station project. The recovery function you provided solved the issue. Have you considered adding a timeout mechanism to the SPI transfers for error detection?</p> <p className={style.commentText}>Excellent article! I've been struggling with I2C bus lockups in my weather station project. The recovery function you provided solved the issue. Have you considered adding a timeout mechanism to the SPI transfers for error detection?</p>
<a href="#" className="comment-reply">Reply</a> <a href="#" className={style.commentReply}>Reply</a>
</div>
</div>
<div className="comment">
<div className="comment-avatar">
<img src="/images/author.jpg" alt="User" />
</div>
<div className="comment-content">
<h4 className="comment-author">SecCodeSmith</h4>
<div className="comment-date">May 5, 2025 at 16:10</div>
<p className="comment-text">@Michael - Good point about the timeout! For SPI transfers, you can implement a timeout using either the HAL Timeout parameter or a hardware timer. For maximum reliability, I prefer using the hardware timer approach as it continues to function even if the main code gets stuck. I'll cover this in a future article on error handling.</p>
<a href="#" className="comment-reply">Reply</a>
</div> </div>
</div> </div>
{/* Comment Form */} {/* Comment Form */}
<div className="comment-form"> <div className={`${style.commentForm} comment-form`}>
<h3 className="form-title">Leave your mark</h3> <h3 className={style.formTitle}>Leave your mark</h3>
<form> <form>
<div className="form-row"> <div className={`form-row ${style.formRow}`}>
<div className="form-group"> <div className={`form-group ${style.formGroup}`}>
<input type="text" className="form-control" placeholder="Your Name" /> <input type="text" className={`${style.formControl}`} placeholder="Your Name" />
</div> </div>
<div className="form-group"> <div className={`form-group ${style.formGroup}`}>
<input type="email" className="form-control" placeholder="Your Email" /> <input type="email" className={`${style.formControl}`} placeholder="Your Email" />
</div> </div>
</div> </div>
<div className="form-group"> <div className={`form-group ${style.formGroup}`}>
<textarea className="form-control" rows={5} placeholder="Your Comment"></textarea> <textarea className={`${style.formControl}`} rows={5} placeholder="Your Comment"></textarea>
</div> </div>
<button type="submit" className="submit-button">Post Comment</button> <button type="submit" className={`submit-button ${style.submitButton}`}>Post Comment</button>
</form> </form>
</div> </div>
</div> </div>
)}
</div>
</article> </article>
</main> </main>
<aside className="post-sidebar"> <aside className={style.postSidebar}>
{/* Table of Contents */} {/* Table of Contents */}
<div className="sidebar-widget"> <div className={style.sidebarWidget}>
<h3 className="widget-title">Table of Contents</h3> <h3 className={style.widgetTitle}>Table of Contents</h3>
<ul className="toc-list"> <TableOfContents toc={toc} />
<li className="toc-item">
<a href="#" className="toc-link">Introduction</a>
</li>
<li className="toc-item">
<a href="#" className="toc-link">The Three Elemental Protocols</a>
</li>
<li className="toc-item">
<a href="#" className="toc-link">Optimizing I2C for Reliable Field Operations</a>
<ul className="toc-list">
<li className="toc-subitem">
<a href="#" className="toc-link">Hardware Incantations</a>
</li>
<li className="toc-subitem">
<a href="#" className="toc-link">Software Rituals</a>
</li>
</ul>
</li>
</ul>
</div> </div>
{/* Related Posts */} {/* Related Posts */}
<div className="sidebar-widget"> <div className={style.sidebarWidget}>
<h3 className="widget-title">Related Scrolls</h3> <h3 className={style.widgetTitle}>Related Scrolls</h3>
<ul className="related-posts-list"> <ul className={style.relatedPostsList}>
{relatedPosts.map(relatedPost => ( {relatedPosts.map(relatedPost => (
<li className="related-post-item" key={relatedPost.id}> <li className={style.relatedPostItem} key={relatedPost.id}>
<Link to={`/blog/${relatedPost.slug}`} className="related-post-link"> <Link to={`/blog/${relatedPost.slug}`} className={style.relatedPostLink}>
<div className="related-post-thumb" style={{ backgroundImage: `url(${relatedPost.image})` }}></div> <div className={style.relatedPostThumb} style={{ backgroundImage: `url(${relatedPost.image})` }}></div>
<div className="related-post-info"> <div className={style.relatedPostInfo}>
<h4 className="related-post-title">{relatedPost.title}</h4> <h4 className={style.relatedPostTitle}>{relatedPost.title}</h4>
<span className="related-post-date">{relatedPost.date}</span> <span className={style.relatedPostDate}>{relatedPost.date}</span>
</div> </div>
</Link> </Link>
</li> </li>
View File
+50 -106
View File
@@ -1,9 +1,14 @@
import { PageHeader } from '../components/PageHeader'; import { PageHeader } from '../components/PageHeader';
import { ContactForm } from '../components/ContactForm'; import { ContactForm } from '../components/ContactForm';
import style from '@styles/Contact.module.scss';
import '@styles/Accordion.scss';
import './Contact.module.css'; import { contactData } from '../data/contactData'
import type { ContactProps } from '../untils/ContactProps';
export const Contact = () => { export const Contact = () => {
const data: ContactProps = contactData;
return ( return (
<> <>
<PageHeader <PageHeader
@@ -22,24 +27,24 @@ export const Contact = () => {
{/* Contact Info Column */} {/* Contact Info Column */}
<div className="col-lg-5"> <div className="col-lg-5">
{/* Email Info */} {/* Email Info */}
<div className="card mb-4"> <div className={`card mb-4 ${style.card}`}>
<div className="card-body p-4"> <div className={`card-body p-4 ${style.cardBody}`}>
<div className="contact-card"> <div className={`${style.contactCard}`}>
<div className="contact-icon"> <div className={`${style.contactIcon}`}>
<i className="fas fa-envelope"></i> <i className="fas fa-envelope"></i>
</div> </div>
<h3 className="contact-title">Email</h3> <h3 className={style.contactTitle}>Email</h3>
<div className="contact-content"> <div className="contact-content">
<p>For project inquiries or questions:</p> <p>For project inquiries or questions:</p>
<p> <p>
<a href="mailto:forge@seccodesmtih.com" className="contact-link"> <a href={`mailto:${data.email}`} className={style.contactLink}>
forge@seccodesmtih.com {data.email}
</a> </a>
</p> </p>
<p>For business collaborations:</p> <p>For business collaborations:</p>
<p> <p>
<a href="mailto:business@seccodesmtih.com" className="contact-link"> <a href={`mailto:${data.businessEmail}`} className={style.contactLink}>
business@seccodesmtih.com {data.businessEmail}
</a> </a>
</p> </p>
</div> </div>
@@ -47,46 +52,34 @@ export const Contact = () => {
</div> </div>
</div> </div>
{/* Connect Through Socials */} {
<div className="card"> data.socialLinks.length > 0 && (
<div className="card-body p-4"> <div className={`card ${style.card}`}>
<div className="contact-card"> <div className={`card-body p-4 ${style.cardBody}`}>
<div className="contact-icon"> <div className={style.contactCard}>
<div className={style.contactIcon}>
<i className="fas fa-link"></i> <i className="fas fa-link"></i>
</div> </div>
<h3 className="contact-title">Connect Through the Ether</h3> <h3 className={style.contactTitle}>Connect Through the Ether</h3>
<div className="contact-content"> <div className={style.contactContent}>
<p>You can also reach me through these digital pathways:</p> <p>You can also reach me through these digital pathways:</p>
<div className="social-grid"> <div className={style.socialGrid}>
<a href="#" className="social-item"> {data.socialLinks.map((link, index) => (
<i className="fab fa-github social-icon-lg"></i>
<span className="social-name">GitHub</span> <a key={index} href={link.url} className={`social-item ${style.socialItem}`}>
</a> <i className={`${link.icon} social-icon-lg ${style.socialIconLg}`}></i>
<a href="#" className="social-item"> <span className={`social-name ${style.socialName}`}>{link.platform}</span>
<i className="fab fa-linkedin social-icon-lg"></i>
<span className="social-name">LinkedIn</span>
</a>
<a href="#" className="social-item">
<i className="fab fa-twitter social-icon-lg"></i>
<span className="social-name">Twitter</span>
</a>
<a href="#" className="social-item">
<i className="fab fa-discord social-icon-lg"></i>
<span className="social-name">Discord</span>
</a>
<a href="#" className="social-item">
<i className="fab fa-telegram social-icon-lg"></i>
<span className="social-name">Telegram</span>
</a>
<a href="#" className="social-item">
<i className="fab fa-stack-overflow social-icon-lg"></i>
<span className="social-name">Stack Overflow</span>
</a> </a>
))}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
)
}
</div> </div>
</div> </div>
</div> </div>
@@ -104,83 +97,34 @@ export const Contact = () => {
<div className="row"> <div className="row">
<div className="col-lg-8 mx-auto"> <div className="col-lg-8 mx-auto">
<div className="accordion" id="faqAccordion"> <div className="accordion" id="faqAccordion">
{/* FAQ Item 1 */} {
<div className="accordion-item"> data.questionsAndAnswers.map((item, index) => (
<h2 className="accordion-header" id="headingOne"> <div className={`accordion-item`} key={index}>
<h2 className={`accordion-header`} id={`heading${index}`}>
<button <button
className="accordion-button collapsed" className={`accordion-button collapsed`}
type="button" type="button"
data-bs-toggle="collapse" data-bs-toggle="collapse"
data-bs-target="#collapseOne" data-bs-target={`#collapse${index}`}
aria-expanded="false" aria-expanded="false"
aria-controls="collapseOne" aria-controls={`collapse${index}`}
> >
What is your typical project timeline? {item.question}
</button> </button>
</h2> </h2>
<div <div
id="collapseOne" id={`collapse${index}`}
className="accordion-collapse collapse" className="accordion-collapse collapse"
aria-labelledby="headingOne" aria-labelledby={`heading${index}`}
data-bs-parent="#faqAccordion" data-bs-parent="#faqAccordion"
> >
<div className="accordion-body"> <div className={`accordion-body`}>
Project timelines vary based on complexity and scope. Small projects typically take 2-4 weeks, medium-sized projects 1-2 months, and larger enterprise solutions 3-6 months. I provide detailed timeline estimates during the initial consultation, and maintain transparent communication about progress throughout the development cycle. {item.answer}
</div>
</div>
</div>
{/* FAQ Item 2 */}
<div className="accordion-item">
<h2 className="accordion-header" id="headingTwo">
<button
className="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseTwo"
aria-expanded="false"
aria-controls="collapseTwo"
>
How does your collaboration process work?
</button>
</h2>
<div
id="collapseTwo"
className="accordion-collapse collapse"
aria-labelledby="headingTwo"
data-bs-parent="#faqAccordion"
>
<div className="accordion-body">
My collaboration process consists of five phases: initial consultation, requirements gathering, design and planning, implementation, and testing/deployment. I use agile methodologies with regular check-ins to ensure your vision is being realized. Communication is maintained through your preferred channels (email, Slack, etc.), and you'll have access to development milestones through shared repositories and project management tools.
</div>
</div>
</div>
{/* FAQ Item 3 */}
<div className="accordion-item">
<h2 className="accordion-header" id="headingThree">
<button
className="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseThree"
aria-expanded="false"
aria-controls="collapseThree"
>
What are your payment terms and methods?
</button>
</h2>
<div
id="collapseThree"
className="accordion-collapse collapse"
aria-labelledby="headingThree"
data-bs-parent="#faqAccordion"
>
<div className="accordion-body">
I typically work with a milestone-based payment structure: 30% upfront to begin work, 30% at the midpoint after key deliverables are approved, and 40% upon project completion. For longer projects, we can establish additional milestones. I accept bank transfers, PayPal, and cryptocurrency payments. All payment terms are clearly outlined in the project contract before work begins.
</div> </div>
</div> </div>
</div> </div>
))
}
</div> </div>
</div> </div>
</div> </div>
@@ -198,9 +142,9 @@ export const Contact = () => {
</div> </div>
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<div className="map-container"> <div className={style.mapContainer}>
<iframe <iframe
className="map-frame" className={style.mapFrame}
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d82077.2903513271!2d21.9433946!3d50.0411861!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x473cfae3cc14d449%3A0xd2240d31b33eb2ed!2zUnplc3rDs3c!5e0!3m2!1sen!2spl!4v1651813309336!5m2!1sen!2spl" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d82077.2903513271!2d21.9433946!3d50.0411861!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x473cfae3cc14d449%3A0xd2240d31b33eb2ed!2zUnplc3rDs3c!5e0!3m2!1sen!2spl!4v1651813309336!5m2!1sen!2spl"
allowFullScreen allowFullScreen
loading="lazy" loading="lazy"
-247
View File
@@ -1,247 +0,0 @@
/* Hero Section */
.hero {
height: 100vh;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
padding-top: 76px;
/* Account for navbar height */
}
.hero::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('/api/placeholder/1200/800') center/cover no-repeat;
opacity: 0.15;
filter: brightness(0.5) contrast(1.2);
z-index: -1;
}
.title-main {
font-size: 4rem;
font-weight: 700;
position: relative;
display: inline-block;
}
.title-main::before {
content: "< ";
color: var(--accent-fire);
opacity: 0.8;
}
.title-main::after {
content: " />";
color: var(--accent-fire);
opacity: 0.8;
}
.subtitle {
font-size: 1.6rem;
color: var(--text-ash);
margin-bottom: 2rem;
}
/* Binary Background */
.binary-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.07;
overflow: hidden;
}
.binary-line {
position: absolute;
font-family: 'Fira Code', monospace;
color: var(--accent-fire);
font-size: 1rem;
animation: fall linear infinite;
opacity: 0.7;
}
/* Button Styling */
.btn-primary {
background: linear-gradient(45deg, var(--accent-dragon), var(--accent-fire));
border: none;
padding: 0.75rem 2rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 4px 15px rgba(139, 0, 0, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 7px 25px rgba(139, 0, 0, 0.5);
background-color: var(--accent-fire);
background-image: none;
}
.btn-primary::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: all 0.6s ease;
}
.btn-primary:hover::before {
left: 100%;
}
/* Skills Section */
.skills-section {
padding: 6rem 0;
background-color: var(--secondary-dark);
position: relative;
}
.section-title {
text-align: center;
font-size: 2.5rem;
margin-bottom: 3rem;
position: relative;
display: inline-block;
}
.section-title::after {
content: "";
position: absolute;
width: 80px;
height: 3px;
background: var(--accent-fire);
bottom: -10px;
left: 50%;
transform: translateX(-50%);
box-shadow: var(--glow-effect);
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: 8px;
transition: all 0.3s ease;
height: 100%;
position: relative;
overflow: hidden;
}
.card:hover {
transform: translateY(-10px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.card::before {
content: "";
position: absolute;
width: 100%;
height: 5px;
top: 0;
left: 0;
background: linear-gradient(to right, var(--accent-dragon), var(--accent-fire));
}
.card-header {
background-color: transparent;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 10px;
}
.category-icon {
color: var(--accent-fire);
font-size: 1.5rem;
}
.category-title {
font-size: 1.4rem;
margin-bottom: 0;
}
.skill-item {
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 12px;
}
.skill-icon {
color: var(--accent-fire);
font-size: 1.5rem;
min-width: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.skill-icon i {
display: inline-flex;
align-items: center;
justify-content: center;
}
.skill-name {
color: var(--text-ash);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* Blinking cursor effect */
.blinking-cursor {
display: inline-block;
width: 10px;
height: 24px;
background-color: var(--accent-fire);
margin-left: 5px;
animation: blink 1s step-end infinite;
box-shadow: var(--glow-effect);
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
/* Particles effect */
.particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.particle {
position: absolute;
width: 3px;
height: 3px;
background-color: var(--accent-fire);
border-radius: 50%;
opacity: 0;
animation: rise linear forwards;
}
+18 -103
View File
@@ -1,44 +1,41 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import styles from './Home.module.css'; import styles from '@styles/Home.module.scss';
import SkillCard from '../components/SkillCard'
import { skillCardData } from '../data/skillCardData';
import { randomCodeLineData } from '../data/randomCodeLineData';
export const Home = () => { export const Home = () => {
// Create binary background and particles effects const skills = skillCardData;
const randomCodeLines = randomCodeLineData;
useEffect(() => { useEffect(() => {
// Create binary background
const createBinaryBg = () => { const createBinaryBg = () => {
const binaryBg = document.getElementById('binary-bg'); const binaryBg = document.getElementById('binary-bg');
if (!binaryBg) return; if (!binaryBg) return;
const chars = '01';
const linesCount = 20; const linesCount = 20;
// Clear existing lines
binaryBg.innerHTML = ''; binaryBg.innerHTML = '';
for (let i = 0; i < linesCount; i++) { for (let i = 0; i < linesCount; i++) {
const line = document.createElement('div'); const line = document.createElement('div');
line.className = styles.binaryLine; line.className = styles.binaryLine;
line.style.left = `${Math.random() * 100}%`; line.style.left = `${Math.random() * 100}%`;
line.style.animationDuration = `${Math.random() * 10 + 15}s`; line.style.animationDuration = `${Math.random() * 10 + 15}s`;
let binaryString = ''; let lineNumber = Math.floor(Math.random() * randomCodeLines.length);
for (let j = 0; j < 100; j++) { let binaryString = randomCodeLines[lineNumber];
binaryString += chars.charAt(Math.floor(Math.random() * chars.length));
}
line.textContent = binaryString; line.textContent = binaryString;
binaryBg.appendChild(line); binaryBg.appendChild(line);
} }
}; };
// Create particles effect (ember/spark effect)
const createParticles = () => { const createParticles = () => {
const particlesContainer = document.getElementById('particles'); const particlesContainer = document.getElementById('particles');
if (!particlesContainer) return; if (!particlesContainer) return;
// Clear existing particles
particlesContainer.innerHTML = ''; particlesContainer.innerHTML = '';
const particlesCount = 50; const particlesCount = 50;
@@ -48,29 +45,24 @@ export const Home = () => {
const particle = document.createElement('div'); const particle = document.createElement('div');
particle.className = styles.particle; particle.className = styles.particle;
// Random position at the bottom of the hero section
const posX = Math.random() * window.innerWidth; const posX = Math.random() * window.innerWidth;
const posY = window.innerHeight - 100 + (Math.random() * 50); const posY = window.innerHeight - 100 + (Math.random() * 50);
particle.style.left = `${posX}px`; particle.style.left = `${posX}px`;
particle.style.top = `${posY}px`; particle.style.top = `${posY}px`;
// Random size
const size = Math.random() * 3 + 1; const size = Math.random() * 3 + 1;
particle.style.width = `${size}px`; particle.style.width = `${size}px`;
particle.style.height = `${size}px`; particle.style.height = `${size}px`;
// Random drift
const xDrift = (Math.random() - 0.5) * 100; const xDrift = (Math.random() - 0.5) * 100;
particle.style.setProperty('--x', `${xDrift}px`); particle.style.setProperty('--x', `${xDrift}px`);
// Random animation duration
const duration = Math.random() * 3 + 2; const duration = Math.random() * 3 + 2;
particle.style.animationDuration = `${duration}s`; particle.style.animationDuration = `${duration}s`;
particlesContainer.appendChild(particle); particlesContainer.appendChild(particle);
// Remove particle after animation completes
setTimeout(() => { setTimeout(() => {
particle.remove(); particle.remove();
}, duration * 1000); }, duration * 1000);
@@ -81,7 +73,6 @@ export const Home = () => {
createBinaryBg(); createBinaryBg();
createParticles(); createParticles();
// Create new particles periodically
const particleInterval = setInterval(() => { const particleInterval = setInterval(() => {
const particlesContainer = document.getElementById('particles'); const particlesContainer = document.getElementById('particles');
if (particlesContainer && particlesContainer.childElementCount < 30) { if (particlesContainer && particlesContainer.childElementCount < 30) {
@@ -97,8 +88,7 @@ export const Home = () => {
return ( return (
<> <>
{/* Hero Section */} <section className={styles.hero} id="home">
<section className="hero" id="home">
<div id="binary-bg" className={styles.binaryBg}></div> <div id="binary-bg" className={styles.binaryBg}></div>
<div className={`container text-center ${styles.heroContainer}`}> <div className={`container text-center ${styles.heroContainer}`}>
<div className="row justify-content-center"> <div className="row justify-content-center">
@@ -106,7 +96,7 @@ export const Home = () => {
<h1 className={`${styles.titleMain}`}>SecCodeSmith</h1> <h1 className={`${styles.titleMain}`}>SecCodeSmith</h1>
<div className={`${styles.blinkingCursor}`}></div> <div className={`${styles.blinkingCursor}`}></div>
<p className={`subtitle mt-3 ${styles.subtitle}`}>Forging Digital Solutions in the Fires of Innovation</p> <p className={`subtitle mt-3 ${styles.subtitle}`}>Forging Digital Solutions in the Fires of Innovation</p>
<Link to="/projects" className={`btn btn-primary mt-4 ${styles.btnPrimary}`}> <Link to="/projects" className={`btn btn-primary mt-4 btn-primary ${styles.btnPrimary}`}>
<i className="fas fa-fire-alt me-2"></i> View My Forge <i className="fas fa-fire-alt me-2"></i> View My Forge
</Link> </Link>
</div> </div>
@@ -115,7 +105,6 @@ export const Home = () => {
<div id="particles" className={`particles ${styles.particles}`}></div> <div id="particles" className={`particles ${styles.particles}`}></div>
</section> </section>
{/* Skills Section */}
<section id="skills" className={`skills-section ${styles.skillsSection}`}> <section id="skills" className={`skills-section ${styles.skillsSection}`}>
<div className="container"> <div className="container">
<div className="row justify-content-center mb-5"> <div className="row justify-content-center mb-5">
@@ -124,87 +113,13 @@ export const Home = () => {
</div> </div>
</div> </div>
<div className="row g-4"> <div className="row g-4">
{/* Languages Card */} {skills.map((skill) => (
<div className="col-md-6 col-lg-4"> <SkillCard
<div className="card h-100"> categoryIcon={skill.categoryIcon}
<div className="card-header"> categoryTitle={skill.categoryTitle}
<i className={`fas fa-code ${styles.categoryIcon}`}></i> skills={skill.skills}
<h3 className="category-title">Languages</h3> />
</div> ))}
<div className="card-body">
<ul className="list-unstyled">
<li className="skill-item">
<span className="skill-icon"><i className="devicon-cplusplus-plain colored"></i></span>
<span className="skill-name">C/C++</span>
</li>
<li className="skill-item">
<span className="skill-icon"><i className="devicon-python-plain colored"></i></span>
<span className="skill-name">Python</span>
</li>
<li className="skill-item">
<span className="skill-icon"><i className="devicon-csharp-plain colored"></i></span>
<span className="skill-name">.NET</span>
</li>
</ul>
</div>
</div>
</div>
{/* Embedded Systems Card */}
<div className="col-md-6 col-lg-4">
<div className="card h-100">
<div className="card-header">
<i className={`fas fa-microchip ${styles.categoryIcon}`}></i>
<h3 className="category-title">Embedded Systems</h3>
</div>
<div className="card-body">
<ul className="list-unstyled">
<li className="skill-item">
<span className="skill-icon"><i className="fas fa-microchip"></i></span>
<span className="skill-name">STM32 (I2C, SPI, UART)</span>
</li>
<li className="skill-item">
<span className="skill-icon"><i className="fas fa-wifi"></i></span>
<span className="skill-name">ESP32</span>
</li>
<li className="skill-item">
<span className="skill-icon"><i className="fas fa-memory"></i></span>
<span className="skill-name">Pico-SDK</span>
</li>
</ul>
</div>
</div>
</div>
{/* Data & ML Card */}
<div className="col-md-6 col-lg-4">
<div className="card h-100">
<div className="card-header">
<i className={`fas fa-brain ${styles.categoryIcon}`}></i>
<h3 className="category-title">Data & ML</h3>
</div>
<div className="card-body">
<ul className="list-unstyled">
<li className="skill-item">
<span className="skill-icon"><i className="devicon-pandas-original colored"></i></span>
<span className="skill-name">Pandas</span>
</li>
<li className="skill-item">
<span className="skill-icon"><i className="fas fa-cogs"></i></span>
<span className="skill-name">Scikit-learn</span>
</li>
<li className="skill-item">
<span className="skill-icon"><i className="fas fa-image"></i></span>
<span className="skill-name">Scikit-image</span>
</li>
<li className="skill-item">
<span className="skill-icon"><i className="devicon-pytorch-original colored"></i></span>
<span className="skill-name">PyTorch</span>
</li>
</ul>
</div>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
+26 -48
View File
@@ -1,30 +1,35 @@
import { useState } from 'react'; import { useState } from 'react';
import { PageHeader } from '../components/PageHeader'; import { PageHeader } from '../components/PageHeader';
import { ProjectCard, type ProjectProps } from '../components/ProjectCard'; import { ProjectCard } from '../components/ProjectCard';
import { ProjectModal } from '../components/ProjectModal'; import { ProjectModal } from '../components/ProjectModal';
import { projectsData } from '../data/projectsData'; import { projectsData, Categories } from '../data/projectsData';
import type {ProjectProps} from '../untils/ProjectProps';
import style from '@styles/Project.module.scss'
export const Projects = () => { export const Projects = () => {
const [activeFilter, setActiveFilter] = useState('all'); const [activeFilter, setActiveFilter] = useState('all');
const [selectedProject, setSelectedProject] = useState<ProjectProps | null>(null); const [selectedProject, setSelectedProject] = useState<ProjectProps | null>(null);
// Filter projects based on selected category
const filteredProjects = activeFilter === 'all' const filteredProjects = activeFilter === 'all'
? projectsData ? projectsData
: projectsData.filter(project => project.category.includes(activeFilter)); : projectsData.filter(project => project.category.some(cat => cat.shortName.toLowerCase() === activeFilter));
if (selectedProject == null && filteredProjects.length > 0)
setSelectedProject(filteredProjects[0]);
const categories = Categories;
// Get the featured project
const featuredProject = projectsData.find(project => project.featured); const featuredProject = projectsData.find(project => project.featured);
// Get regular projects (non-featured)
const regularProjects = filteredProjects.filter(project => !project.featured); const regularProjects = filteredProjects.filter(project => !project.featured);
// Handle project filter click
const handleFilterClick = (filter: string) => { const handleFilterClick = (filter: string) => {
setActiveFilter(filter); setActiveFilter(filter);
}; };
// Handle opening project details modal
const handleOpenDetails = (id: string) => { const handleOpenDetails = (id: string) => {
const project = projectsData.find(p => p.id === id); const project = projectsData.find(p => p.id === id);
if (project) { if (project) {
@@ -32,11 +37,6 @@ export const Projects = () => {
} }
}; };
// Handle closing project details modal
const handleCloseDetails = () => {
setSelectedProject(null);
};
return ( return (
<> <>
<PageHeader <PageHeader
@@ -46,74 +46,52 @@ export const Projects = () => {
<div className="container mt-5"> <div className="container mt-5">
{/* Projects Filter */} {/* Projects Filter */}
<div className="filter-container text-center"> <div className={`filter-container ${style.filterContainer} text-center`}>
<button <button
className={`filter-button ${activeFilter === 'all' ? 'active' : ''}`} className={`filter-button ${style.filterButton} ${activeFilter === 'all' ? style.active : ''}`}
onClick={() => handleFilterClick('all')} onClick={() => handleFilterClick('all')}
> >
All Artifacts All Artifacts
</button> </button>
{Object.values(categories).map((category) => (
<button <button
className={`filter-button ${activeFilter === 'embedded' ? 'active' : ''}`} key={category.shortName}
onClick={() => handleFilterClick('embedded')} className={`filter-button ${style.filterButton} ${activeFilter === category.shortName.toLowerCase() ? style.active : ''}`}
onClick={() => handleFilterClick(category.shortName.toLowerCase())}
> >
Embedded Systems {category.fullName}
</button>
<button
className={`filter-button ${activeFilter === 'web' ? 'active' : ''}`}
onClick={() => handleFilterClick('web')}
>
Web Development
</button>
<button
className={`filter-button ${activeFilter === 'ml' ? 'active' : ''}`}
onClick={() => handleFilterClick('ml')}
>
Machine Learning
</button>
<button
className={`filter-button ${activeFilter === 'iot' ? 'active' : ''}`}
onClick={() => handleFilterClick('iot')}
>
IoT
</button> </button>
))}
</div> </div>
{/* Projects Grid */}
<div className="row"> <div className="row">
{/* Featured Project */} {featuredProject && (activeFilter === 'all' || featuredProject.category.some(cat => cat.shortName.toLowerCase() === activeFilter)) && (
{featuredProject && (activeFilter === 'all' || featuredProject.category.includes(activeFilter)) && (
<div className="col-12 mb-4"> <div className="col-12 mb-4">
<ProjectCard project={featuredProject} onOpenDetails={handleOpenDetails} /> <ProjectCard project={featuredProject} onOpenDetails={handleOpenDetails} />
</div> </div>
)} )}
{/* Regular Projects */}
{regularProjects.map(project => ( {regularProjects.map(project => (
<div className="col-md-6 col-lg-4 mb-4" key={project.id}>
<ProjectCard project={project} onOpenDetails={handleOpenDetails} /> <ProjectCard project={project} onOpenDetails={handleOpenDetails} />
</div>
))} ))}
</div> </div>
{/* No Projects Message */}
{filteredProjects.length === 0 && ( {filteredProjects.length === 0 && (
<div className="text-center mt-5"> <div className="text-center mt-5">
<h3>No projects found for this category.</h3> <h3>No projects found for this category.</h3>
</div> </div>
)} )}
{/* View All Button */} <div className="text-center mt-4 mb-5">
<div className="text-center mt-4"> <a href="https://github.com/SecCodeSmith" className={`btn btn-primary ${style.btnPrimary}`}>
<a href="https://github.com/SecCodeSmith" className="btn btn-primary">
<i className="fab fa-github me-2"></i> View All Projects on GitHub <i className="fab fa-github me-2"></i> View All Projects on GitHub
</a> </a>
</div> </div>
</div> </div>
{/* Project Modal */}
{selectedProject && ( {selectedProject && (
<ProjectModal project={selectedProject} onClose={handleCloseDetails} /> <ProjectModal project={selectedProject} />
)} )}
</> </>
); );
+30
View File
@@ -0,0 +1,30 @@
export interface AboutProps {
coreValues: CoreValue[];
technicalArsenal: TechnicalArsenal[];
professionalJourney: ProfessionalJourney[];
testimonials: Testimonial[];
}
export interface CoreValue {
title: string;
description: string;
icon: string;
}
export interface ProfessionalJourney {
title: string;
description: string;
duration: string;
}
export interface TechnicalArsenal {
title: string;
icon: string;
skills: string[];
}
export interface Testimonial {
content: string;
author: string;
position: string;
}
+16
View File
@@ -0,0 +1,16 @@
export interface BlogPostProps {
id: string;
slug: string;
title: string;
excerpt: string;
image: string;
category: string;
date: string;
author: string;
commentCount: number;
readTime: string;
featured?: boolean;
tags: string[];
content: string;
}
+19
View File
@@ -0,0 +1,19 @@
export interface ContactProps {
email: string;
businessEmail: string;
socialLinks: SocialLink[];
questionsAndAnswers: QuestionAndAnswer[];
MapIframeUrl: string;
phone: string;
}
export interface SocialLink {
platform: string;
url: string;
icon: string;
}
export interface QuestionAndAnswer {
question: string;
answer: string;
}
+46
View File
@@ -0,0 +1,46 @@
export interface ProjectProps {
id: string;
title: string;
description: string;
projectDetails?: ProjectDetails;
image: string;
category: Category[];
featured?: boolean;
technologies: ProjectTech[];
links: {
github?: string;
demo?: string;
documentation?: string;
};
}
export interface ProjectDetails {
descriptions?: string[];
startDate?: string;
endDate?: string;
dateFormatted?: string;
role?: string;
status?: string;
client?: string;
keyFeatures?: string[];
gallery?: string[];
fullTechStack?: FullTechStack[];
}
export interface Category {
[key: string]: string;
fullName: string;
shortName: string;
icon: string;
}
export interface ProjectTech {
name: string;
icon: string;
}
export interface FullTechStack {
name: string;
icons: string;
}
+10
View File
@@ -0,0 +1,10 @@
export interface Skill {
name: string;
icon: React.ReactNode; // Or string if using CSS classes for icons
}
export interface SkillCardProps {
categoryTitle: string;
categoryIcon: React.ReactNode; // Or string
skills: Skill[];
}
+4
View File
@@ -0,0 +1,4 @@
export interface SocialLink {
icon: string;
url: string;
}
+41 -2
View File
@@ -1,7 +1,46 @@
{ {
"files": [], "files": [],
"references": [ "references": [
{ "path": "./tsconfig.app.json" }, {
{ "path": "./tsconfig.node.json" } "path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
],
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
],
"@assets/*": [
"src/assets/*"
],
"@components/*": [
"src/components/*"
],
"@pages/*": [
"src/pages/*"
],
"@data/*": [
"src/data/*"
],
"@styles/*": [
"src/styles/*"
],
"@tests/*": [
"src/tests/*"
],
"@images/*": [
"src/assets/images/*"
],
},
"moduleResolution": "node",
"types": [
"node"
] ]
} }
}
+25 -1
View File
@@ -1,7 +1,31 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import path from 'path'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], resolve: {
alias: {
"@": path.resolve(__dirname, "/src"),
'@assets': path.resolve(__dirname, '/src/assets'),
'@components': path.resolve(__dirname, '/src/components'),
'@pages': path.resolve(__dirname, '/src/pages'),
'@data': path.resolve(__dirname, '/src/data'),
'@styles': path.resolve(__dirname, '/src/assets/styles'),
'@public': path.resolve(__dirname, '/src/public'),
'@tests': path.resolve(__dirname, '/src/tests'),
'@images': path.resolve(__dirname, '/src/assets/images'),
}
},
plugins: [
react(),
],
publicDir: 'public',
build: {
target: ['es2022', 'chrome100', 'firefox100', 'safari15']
},
}) })