Refactor: Extract CSS and JavaScript into external files for reusability
- Create professional-theme.css with complete soft professional blue theme - Soft professional color palette with reduced saturation - Professional card styling with hover effects - Button styles (primary, secondary, urgent) with animations - Navbar, hero section, and support section styles - Dark mode support and mobile responsiveness - Create professional-theme.js with comprehensive functionality - Theme toggle with localStorage persistence - Card hover animations with 3D effects - Scroll animations using Intersection Observer - Button ripple effects and smooth scrolling - WebP detection and utility functions - Additional features: loading states, toast notifications, scroll-to-top - Update index.html to use external files instead of inline styles/scripts - Replace Bootstrap with Tailwind CSS and Flowbite - Implement professional hero section with call-to-action buttons - Create featured ROA AUTO card spanning 2 columns with problems list and image - Add ROA PROIECTE DEVIZE LUCRĂRI card for construction cost estimation - Professional support tools section with enhanced styling - Add CLAUDE.md with project documentation and development guidelines - Architecture overview and development commands - CSS architecture with custom properties system - Frontend design workflow and styling guidelines 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
395
professional-theme.js
Normal file
395
professional-theme.js
Normal file
@@ -0,0 +1,395 @@
|
||||
// Professional Theme JavaScript - Romfast Applications
|
||||
// This file contains all interactive functionality for the professional theme
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeProfessionalTheme();
|
||||
});
|
||||
|
||||
function initializeProfessionalTheme() {
|
||||
// Initialize all components
|
||||
initializeLucideIcons();
|
||||
initializeThemeToggle();
|
||||
initializeCardAnimations();
|
||||
initializeScrollAnimations();
|
||||
initializeButtonEffects();
|
||||
initializeHeroAnimations();
|
||||
initializeSmoothScrolling();
|
||||
initializeWebPSupport();
|
||||
}
|
||||
|
||||
// Initialize Lucide icons
|
||||
function initializeLucideIcons() {
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced theme toggle functionality
|
||||
function initializeThemeToggle() {
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
const body = document.body;
|
||||
|
||||
if (!themeToggle) return;
|
||||
|
||||
// Check for saved theme preference or default to light mode
|
||||
const currentTheme = localStorage.getItem('theme') || 'light';
|
||||
if (currentTheme === 'dark') {
|
||||
body.classList.add('dark-mode');
|
||||
themeToggle.innerHTML = '<i data-lucide="moon" class="w-5 h-5"></i>';
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
themeToggle.addEventListener('click', () => {
|
||||
body.classList.toggle('dark-mode');
|
||||
|
||||
// Update icon and save preference
|
||||
if (body.classList.contains('dark-mode')) {
|
||||
themeToggle.innerHTML = '<i data-lucide="moon" class="w-5 h-5"></i>';
|
||||
localStorage.setItem('theme', 'dark');
|
||||
} else {
|
||||
themeToggle.innerHTML = '<i data-lucide="sun" class="w-5 h-5"></i>';
|
||||
localStorage.setItem('theme', 'light');
|
||||
}
|
||||
|
||||
// Reinitialize icons after DOM change
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Professional card hover animations
|
||||
function initializeCardAnimations() {
|
||||
document.querySelectorAll('.professional-card').forEach(card => {
|
||||
card.addEventListener('mouseenter', (e) => {
|
||||
const rect = card.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const mouseX = e.clientX;
|
||||
const mouseY = e.clientY;
|
||||
|
||||
const deltaX = (mouseX - centerX) * 0.05;
|
||||
const deltaY = (mouseY - centerY) * 0.05;
|
||||
|
||||
card.style.transform = `translateY(-4px) rotateX(${-deltaY}deg) rotateY(${deltaX}deg)`;
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', () => {
|
||||
card.style.transform = 'translateY(0) rotateX(0deg) rotateY(0deg)';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Intersection Observer for scroll animations
|
||||
function initializeScrollAnimations() {
|
||||
const observerOptions = {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -50px 0px'
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.style.opacity = '1';
|
||||
entry.target.style.transform = 'translateY(0)';
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
// Apply scroll animations to cards
|
||||
document.querySelectorAll('.professional-card').forEach((card, index) => {
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'translateY(20px)';
|
||||
card.style.transition = `opacity 0.6s ease ${index * 0.1}s, transform 0.6s ease ${index * 0.1}s`;
|
||||
observer.observe(card);
|
||||
});
|
||||
|
||||
// Apply scroll animations to support cards
|
||||
document.querySelectorAll('.professional-support-card').forEach((card, index) => {
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'translateY(20px)';
|
||||
card.style.transition = `opacity 0.6s ease ${index * 0.15}s, transform 0.6s ease ${index * 0.15}s`;
|
||||
observer.observe(card);
|
||||
});
|
||||
}
|
||||
|
||||
// Professional button interactions with enhanced effects
|
||||
function initializeButtonEffects() {
|
||||
// Add ripple animation keyframes to document
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes ripple {
|
||||
to {
|
||||
transform: scale(3);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Add click ripple effect to buttons
|
||||
document.querySelectorAll('.professional-btn-primary, .professional-btn-secondary, .professional-btn-urgent').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
createRippleEffect(btn, e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Create ripple effect for button clicks
|
||||
function createRippleEffect(button, event) {
|
||||
const ripple = document.createElement('div');
|
||||
const rect = button.getBoundingClientRect();
|
||||
const size = Math.max(rect.width, rect.height);
|
||||
const x = event.clientX - rect.left - size / 2;
|
||||
const y = event.clientY - rect.top - size / 2;
|
||||
|
||||
ripple.style.cssText = `
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
width: ${size}px;
|
||||
height: ${size}px;
|
||||
left: ${x}px;
|
||||
top: ${y}px;
|
||||
animation: ripple 0.5s linear;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
button.style.position = 'relative';
|
||||
button.style.overflow = 'hidden';
|
||||
button.appendChild(ripple);
|
||||
|
||||
setTimeout(() => {
|
||||
ripple.remove();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Subtle floating animation for hero background
|
||||
function initializeHeroAnimations() {
|
||||
const hero = document.querySelector('.professional-hero');
|
||||
if (!hero) return;
|
||||
|
||||
let mouseX = 0;
|
||||
let mouseY = 0;
|
||||
|
||||
hero.addEventListener('mousemove', (e) => {
|
||||
const rect = hero.getBoundingClientRect();
|
||||
mouseX = (e.clientX - rect.left) / rect.width;
|
||||
mouseY = (e.clientY - rect.top) / rect.height;
|
||||
|
||||
const translateX = (mouseX - 0.5) * 10;
|
||||
const translateY = (mouseY - 0.5) * 10;
|
||||
hero.style.setProperty('--mouse-x', `${translateX}px`);
|
||||
hero.style.setProperty('--mouse-y', `${translateY}px`);
|
||||
});
|
||||
}
|
||||
|
||||
// Enhanced smooth scrolling for anchor links
|
||||
function initializeSmoothScrolling() {
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
target.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// WebP support detection
|
||||
function initializeWebPSupport() {
|
||||
function checkWebPSupport() {
|
||||
const webP = new Image();
|
||||
webP.onload = webP.onerror = function () {
|
||||
if (webP.height === 2) {
|
||||
document.documentElement.classList.add('webp');
|
||||
}
|
||||
};
|
||||
webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
|
||||
}
|
||||
|
||||
checkWebPSupport();
|
||||
}
|
||||
|
||||
// Utility function to reinitialize components (useful for dynamic content)
|
||||
function reinitializeProfessionalTheme() {
|
||||
initializeLucideIcons();
|
||||
initializeCardAnimations();
|
||||
initializeButtonEffects();
|
||||
}
|
||||
|
||||
// Navigation dropdown functionality
|
||||
function initializeNavigation() {
|
||||
// Handle dropdown menus
|
||||
document.querySelectorAll('.dropdown').forEach(dropdown => {
|
||||
const button = dropdown.querySelector('[data-dropdown-toggle]');
|
||||
const menu = dropdown.querySelector('.dropdown-menu');
|
||||
|
||||
if (button && menu) {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
menu.classList.toggle('show');
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!dropdown.contains(e.target)) {
|
||||
menu.classList.remove('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Mobile menu functionality
|
||||
function initializeMobileMenu() {
|
||||
const mobileMenuButton = document.querySelector('[data-mobile-menu-toggle]');
|
||||
const mobileMenu = document.querySelector('[data-mobile-menu]');
|
||||
|
||||
if (mobileMenuButton && mobileMenu) {
|
||||
mobileMenuButton.addEventListener('click', () => {
|
||||
mobileMenu.classList.toggle('show');
|
||||
mobileMenuButton.setAttribute('aria-expanded',
|
||||
mobileMenu.classList.contains('show') ? 'true' : 'false'
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Form enhancements
|
||||
function initializeForms() {
|
||||
// Add floating label effects
|
||||
document.querySelectorAll('.form-floating input, .form-floating textarea').forEach(input => {
|
||||
input.addEventListener('focus', () => {
|
||||
input.parentElement.classList.add('focused');
|
||||
});
|
||||
|
||||
input.addEventListener('blur', () => {
|
||||
if (!input.value) {
|
||||
input.parentElement.classList.remove('focused');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize state for pre-filled inputs
|
||||
if (input.value) {
|
||||
input.parentElement.classList.add('focused');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Loading state management
|
||||
function showLoading(element) {
|
||||
if (!element) return;
|
||||
|
||||
element.classList.add('loading');
|
||||
element.disabled = true;
|
||||
|
||||
const originalText = element.textContent;
|
||||
element.setAttribute('data-original-text', originalText);
|
||||
element.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin mr-2"></i>Loading...';
|
||||
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
}
|
||||
|
||||
function hideLoading(element) {
|
||||
if (!element) return;
|
||||
|
||||
element.classList.remove('loading');
|
||||
element.disabled = false;
|
||||
|
||||
const originalText = element.getAttribute('data-original-text');
|
||||
if (originalText) {
|
||||
element.textContent = originalText;
|
||||
element.removeAttribute('data-original-text');
|
||||
}
|
||||
}
|
||||
|
||||
// Toast notification system
|
||||
function showToast(message, type = 'info', duration = 5000) {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.innerHTML = `
|
||||
<div class="toast-content">
|
||||
<i data-lucide="${getToastIcon(type)}" class="w-5 h-5"></i>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
<button class="toast-close" onclick="this.parentElement.remove()">
|
||||
<i data-lucide="x" class="w-4 h-4"></i>
|
||||
</button>
|
||||
`;
|
||||
|
||||
// Add toast container if it doesn't exist
|
||||
let container = document.querySelector('.toast-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.className = 'toast-container';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
container.appendChild(toast);
|
||||
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
// Auto remove after duration
|
||||
setTimeout(() => {
|
||||
toast.remove();
|
||||
}, duration);
|
||||
}
|
||||
|
||||
function getToastIcon(type) {
|
||||
const icons = {
|
||||
success: 'check-circle',
|
||||
error: 'alert-circle',
|
||||
warning: 'alert-triangle',
|
||||
info: 'info'
|
||||
};
|
||||
return icons[type] || icons.info;
|
||||
}
|
||||
|
||||
// Scroll-to-top functionality
|
||||
function initializeScrollToTop() {
|
||||
const scrollButton = document.createElement('button');
|
||||
scrollButton.className = 'scroll-to-top professional-btn-primary';
|
||||
scrollButton.innerHTML = '<i data-lucide="arrow-up" class="w-5 h-5"></i>';
|
||||
scrollButton.setAttribute('aria-label', 'Scroll to top');
|
||||
document.body.appendChild(scrollButton);
|
||||
|
||||
// Show/hide button based on scroll position
|
||||
window.addEventListener('scroll', () => {
|
||||
if (window.pageYOffset > 300) {
|
||||
scrollButton.classList.add('show');
|
||||
} else {
|
||||
scrollButton.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Scroll to top functionality
|
||||
scrollButton.addEventListener('click', () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
}
|
||||
|
||||
// Export functions for global use
|
||||
window.ProfessionalTheme = {
|
||||
reinitialize: reinitializeProfessionalTheme,
|
||||
showLoading: showLoading,
|
||||
hideLoading: hideLoading,
|
||||
showToast: showToast,
|
||||
createRippleEffect: createRippleEffect
|
||||
};
|
||||
Reference in New Issue
Block a user