- 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>
395 lines
12 KiB
JavaScript
395 lines
12 KiB
JavaScript
// 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
|
|
}; |