diff --git a/css/styles.css b/css/styles.css new file mode 100644 index 0000000..ac44f2d --- /dev/null +++ b/css/styles.css @@ -0,0 +1,37 @@ +/* Glass Morphism Effect */ +.glass { + background: rgba(255, 255, 255, 0.4); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.dark .glass { + background: rgba(30, 20, 40, 0.4); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +/* Iridescent Background Gradient */ +.iridescent-bg { + background: + radial-gradient( + circle at 0% 0%, + rgba(140, 43, 238, 0.15) 0%, + transparent 50% + ), + radial-gradient( + circle at 100% 100%, + rgba(255, 182, 255, 0.2) 0%, + transparent 50% + ), + radial-gradient( + circle at 50% 50%, + rgba(140, 43, 238, 0.05) 0%, + transparent 100% + ); +} + +/* Fluid Blob Shapes */ +.fluid-shape { + border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; +} diff --git a/index.html b/index.html index d5ebf72..be0447e 100644 --- a/index.html +++ b/index.html @@ -37,39 +37,7 @@ }, }; - + - + diff --git a/js/language-toggle.js b/js/language-toggle.js deleted file mode 100644 index 8ef8ab0..0000000 --- a/js/language-toggle.js +++ /dev/null @@ -1,20 +0,0 @@ -(function () { - const savedLang = localStorage.getItem("language") || "en"; - const htmlLang = document.documentElement.lang || "en"; - const currentLang = savedLang || htmlLang; - - window.setLanguage = function (lang) { - localStorage.setItem("language", lang); - location.reload(); - }; - - window.getCurrentLanguage = function () { - return localStorage.getItem("language") || "en"; - }; - - window.toggleLanguage = function () { - const currentLang = getCurrentLanguage(); - const newLang = currentLang === "en" ? "es" : "en"; - setLanguage(newLang); - }; -})(); diff --git a/js/main.js b/js/main.js index 69fa380..000feb5 100644 --- a/js/main.js +++ b/js/main.js @@ -1,280 +1,107 @@ -// Translation data -const translations = { - en: { - hero: { - title: "Vanguard Designer", - subtitle: "Crafting Digital Excellence" - }, - about: { - title: "About Me", - description: "A sophisticated designer specializing in high-end digital experiences with a focus on dark aesthetics, glassmorphism, and modern typography. Transforming complex problems into elegant, intuitive interfaces.", - downloadCV: "Download CV", - contact: "Contact" - }, - skills: { - title: "Core Expertise" - }, - projects: { - title: "Latest Work", - viewAll: "View All" - }, - experience: { - title: "Experience" - } - }, - es: { - hero: { - title: "Diseñadora Vanguardista", - subtitle: "Creando Excelencia Digital" - }, - about: { - title: "Sobre Mí", - description: "Una diseñadora sofisticada especializada en experiencias digitales de alta gama con un enfoque en estéticas oscuras, glassmorfismo y tipografía moderna. Transformando problemas complejos en interfaces elegantes e intuitivas.", - downloadCV: "Descargar CV", - contact: "Contacto" - }, - skills: { - title: "Competencias Clave" - }, - projects: { - title: "Trabajos Recientes", - viewAll: "Ver Todos" - }, - experience: { - title: "Experiencia" - } - } -}; +// ============================================ +// Theme Toggle - Dark/Light Mode +// ============================================ -// Projects data -const projects = [ - { - id: 1, - title: { - en: 'Luminary App', - es: 'App Luminary' - }, - description: { - en: 'A fintech dashboard redesign for high-net-worth individuals.', - es: 'Un rediseño de dashboard fintech para individuos de alto patrimonio.' - }, - url: 'luminary-project.co', - image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuDuiRGNBP07ZrmaH7F-Q5XCG8XcmSnVisQQn-Tq4zMmt4oxFQYhnVmEZ16pqAEXpXmPJ29Xc42MlLHJHMla4Q1dMWqm2apfbWMmKQW4gH3fD35Piy_YM1xG2DEHO_es1DqVWQ1GzgmjaRrLehiCzLynVjvtlIg7p5NL8kDBwKBkPqk_nYfYK7jv1lDBrVN4AZ1Hz_j_sRfkerkDWmP2oaTSQh0gN2DLNyN-r34Nda4Dp3GWFWRjZSlivobGKj0mj0eg7QK_Fqcp5GU' - }, - { - id: 2, - title: { - en: 'Ether OS', - es: 'Sistema Ether' - }, - description: { - en: 'A minimalist operating system concept based on spatial UI.', - es: 'Un concepto de sistema operativo minimalista basado en UI espacial.' - }, - url: 'ether-spatial.io', - image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuDvWd-Z_6rj37HlgWlrmx1VJHroq2bg6Pv0atKO0GnAF5_74-yfWaASnLWaWh5J6NiThTvt1W3avgzNI3bOpEG0GoVeYjwZxqMwZZRIiaaODFunpArG-H2otP37dkbfuWzOP_QoEWn6INmZ8YFm50k3YDvoCyZNrnv1dETjWlyg2cok9MP4IIqJLDVxp5Yi32R9BUDgxGlVWLUceUR71hTQRKQqKI9jsQPH7E0Y1OLKeCOZ1Wo-RKabzipc2Vtvmo6RtGvT2X0VoSE' - } -]; - -// Experience data -const experience = [ - { - period: '2022 - Present', - title: { - en: 'Senior UX Designer', - es: 'Diseñadora UX Senior' - }, - company: 'Studio Vanta • Remote', - description: { - en: 'Leading the design language for next-gen SaaS products and immersive web experiences.', - es: 'Liderando el lenguaje de diseño para productos SaaS de próxima generación y experiencias web inmersivas.' - }, - isActive: true, - }, - { - period: '2020 - 2022', - title: { - en: 'Product Designer', - es: 'Diseñadora de Producto' - }, - company: 'Flow Digital • Madrid', - description: { - en: 'Conceptualized and launched 4 mobile applications with over 1M combined downloads.', - es: 'Conceptualicé y lancé 4 aplicaciones móviles con más de 1M de descargas combinadas.' - }, - isActive: false, - } -]; - -// Theme Management -class ThemeManager { - constructor() { - this.theme = localStorage.getItem('theme') || this.getSystemPreference(); - this.init(); - } - - getSystemPreference() { - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; - } - - init() { - this.applyTheme(); - this.setupEventListeners(); - } - - applyTheme() { - document.documentElement.classList.toggle('dark', this.theme === 'dark'); - this.updateThemeIcon(); - } - - updateThemeIcon() { - const icon = document.querySelector('#theme-toggle .material-symbols-outlined'); - if (icon) { - icon.textContent = this.theme === 'dark' ? 'light_mode' : 'dark_mode'; - } - } - - toggle() { - this.theme = this.theme === 'dark' ? 'light' : 'dark'; - localStorage.setItem('theme', this.theme); - this.applyTheme(); - } - - setupEventListeners() { - const themeToggle = document.getElementById('theme-toggle'); - if (themeToggle) { - themeToggle.addEventListener('click', () => this.toggle()); - } - } +/** + * Initialize theme on page load + * Checks localStorage for saved preference, falls back to light mode + */ +function initTheme() { + const savedTheme = localStorage.getItem("theme") || "light"; + if (savedTheme === "dark") { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } } -// Language Management -class LanguageManager { - constructor() { - this.currentLang = localStorage.getItem('language') || 'en'; - this.init(); - } - - init() { - this.applyLanguage(); - this.setupEventListeners(); - this.renderDynamicContent(); - } - - applyLanguage() { - document.documentElement.lang = this.currentLang; - - // Update all elements with data-i18n attribute - document.querySelectorAll('[data-i18n]').forEach(element => { - const key = element.getAttribute('data-i18n'); - const translation = this.getTranslation(key); - if (translation) { - element.textContent = translation; - } - }); - } - - getTranslation(key) { - const keys = key.split('.'); - let value = translations[this.currentLang]; - - for (const k of keys) { - value = value?.[k]; - } - - return value || key; - } - - toggle() { - this.currentLang = this.currentLang === 'en' ? 'es' : 'en'; - localStorage.setItem('language', this.currentLang); - this.applyLanguage(); - this.renderDynamicContent(); - } - - renderDynamicContent() { - this.renderProjects(); - this.renderExperience(); - } - - renderProjects() { - const container = document.getElementById('projects-container'); - if (!container) return; - - container.innerHTML = projects.map(project => ` -
-
-
-

${project.title[this.currentLang]}

-

${project.description[this.currentLang]}

-
- link - ${project.url} -
-
-
- `).join(''); - } - - renderExperience() { - const container = document.getElementById('experience-container'); - if (!container) return; - - container.innerHTML = ` -
- ${experience.map(job => ` -
-
-
-
- ${job.period} -

${job.title[this.currentLang]}

-

${job.company}

-

${job.description[this.currentLang]}

-
- `).join('')} -
- `; - } - - setupEventListeners() { - const languageToggle = document.getElementById('language-toggle'); - if (languageToggle) { - languageToggle.addEventListener('click', () => this.toggle()); - } - } +/** + * Toggle between dark and light theme + * Saves preference to localStorage + */ +function toggleTheme() { + const html = document.documentElement; + if (html.classList.contains("dark")) { + html.classList.remove("dark"); + localStorage.setItem("theme", "light"); + } else { + html.classList.add("dark"); + localStorage.setItem("theme", "dark"); + } } -// Initialize everything when DOM is ready -document.addEventListener('DOMContentLoaded', () => { - new ThemeManager(); - new LanguageManager(); - - // Add smooth scrolling - 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' }); - } - }); - }); - - // Add fade-in animation to sections - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.classList.add('animate-fade-in'); - } - }); - }, { threshold: 0.1 }); - - document.querySelectorAll('section').forEach(section => { - observer.observe(section); +// ============================================ +// Smooth Scrolling +// ============================================ + +/** + * Scroll to projects section + */ +function scrollToProjects() { + const projectsSection = document.getElementById("projects"); + if (projectsSection) { + projectsSection.scrollIntoView({ behavior: "smooth" }); + } +} + +/** + * Scroll to contact section + */ +function scrollToContact() { + const contactSection = document.getElementById("contact"); + if (contactSection) { + contactSection.scrollIntoView({ behavior: "smooth" }); + } +} + +// ============================================ +// Modal Management +// ============================================ + +/** + * Open modal by project ID + * @param {string} projectId - The project identifier + */ +function openModal(projectId) { + const modal = document.getElementById(projectId + "-modal"); + if (modal) { + modal.classList.remove("hidden"); + document.body.style.overflow = "hidden"; + } +} + +/** + * Close modal by project ID + * @param {string} projectId - The project identifier + */ +function closeModal(projectId) { + const modal = document.getElementById(projectId + "-modal"); + if (modal) { + modal.classList.add("hidden"); + document.body.style.overflow = "auto"; + } +} + +// ============================================ +// Event Listeners +// ============================================ + +/** + * Close modal when ESC key is pressed + */ +document.addEventListener("keydown", function (event) { + if (event.key === "Escape") { + const modals = document.querySelectorAll( + '[id$="-modal"]:not(.hidden)' + ); + modals.forEach((modal) => { + const projectId = modal.id.replace("-modal", ""); + closeModal(projectId); }); + } }); + +/** + * Initialize theme when DOM is ready + */ +document.addEventListener("DOMContentLoaded", initTheme); diff --git a/js/theme-toggle.js b/js/theme-toggle.js deleted file mode 100644 index c9b653a..0000000 --- a/js/theme-toggle.js +++ /dev/null @@ -1,15 +0,0 @@ -(function() { - const theme = localStorage.getItem('theme'); - const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - const initialTheme = theme || (systemPrefersDark ? 'dark' : 'light'); - - document.documentElement.classList.toggle('dark', initialTheme === 'dark'); - - window.toggleTheme = function() { - const currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light'; - const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; - - document.documentElement.classList.toggle('dark'); - localStorage.setItem('theme', newTheme); - }; -})();