import gradio as gr import base64 import os # --- Helper Functions --- def file_to_data_uri(filepath, mime_type="application/pdf"): with open(filepath, "rb") as f: data = f.read() b64 = base64.b64encode(data).decode("utf-8") return f"data:{mime_type};base64,{b64}" # --- Navigation Functions --- def show_data_analytics(): return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False) def show_machine_learning(): return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False) def show_computer_vision(): return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True) def go_home(): return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) # --- Icons (SVG) --- data_analytics_icon = """""" machine_learning_icon = """""" computer_vision_icon = """""" home_icon = """""" linkedin_icon = """""" github_icon = """""" mail_icon = """""" link_icon = """""" document_icon = """""" # --- Function to encode image --- def image_to_data_uri(filepath, mime_type="image/jpeg"): with open(filepath, "rb") as f: data = f.read() b64 = base64.b64encode(data).decode("utf-8") return f"data:{mime_type};base64,{b64}" # --- CSS --- portfolio_css = """ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Montserrat:wght@700;800&display=swap'); :root { --primary-da: #8a2be2; --secondary-da: #2575fc; --primary-ml: #00b4db; --secondary-ml: #0083b0; --primary-cv: #ff4d7e; --secondary-cv: #fd3e58; --dark-bg: #0f1118; --card-bg: #1a1d29; --text-primary: #ffffff; --text-secondary: #e0e0e0; --text-muted: #a0a0a0; --shadow-sm: 0 4px 6px rgba(0, 0, 0, 0.1); --shadow-md: 0 8px 16px rgba(0, 0, 0, 0.2); --shadow-lg: 0 12px 24px rgba(0, 0, 0, 0.2); --border-radius-sm: 8px; --border-radius-md: 12px; --border-radius-lg: 20px; --transition-fast: 0.2s ease; --transition-med: 0.3s ease; --transition-slow: 0.5s ease; } body { font-family: 'Poppins', sans-serif; background: var(--dark-bg); background-image: radial-gradient(circle at 25% 25%, rgba(53, 53, 113, 0.05) 0%, transparent 50%), radial-gradient(circle at 75% 75%, rgba(113, 53, 53, 0.05) 0%, transparent 50%); color: var(--text-primary); margin: 0; padding: 0; overflow-x: hidden; } .gr-container { max-width: 1200px; margin: 0 auto; padding: 20px; } /* Scrollbar styling */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.05); border-radius: 4px; } ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.3); } /* Landing section */ .landing-section { text-align: center; margin-bottom: 60px; padding: 40px 20px; position: relative; } .landing-section:before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 500px; background: linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 100%); z-index: -1; } .landing-section h1, .landing-section h2 { color: var(--text-primary) !important; margin-top: 0; } .landing-section h1 { font-family: 'Montserrat', sans-serif; font-size: 3.2rem; font-weight: 800; margin-bottom: 0.5rem; background: linear-gradient(90deg, var(--primary-da), var(--primary-ml), var(--primary-cv)); -webkit-background-clip: text; background-clip: text; color: transparent !important; letter-spacing: -0.5px; } .landing-section h2 { font-size: 2rem; font-weight: 600; margin-bottom: 1.5rem; } .profile-container { margin: 30px auto; display: flex; align-items: center; justify-content: center; flex-direction: column; } .profile-pic { width: 180px; height: 180px; border-radius: 50%; object-fit: cover; border: 4px solid rgba(255, 255, 255, 0.2); box-shadow: var(--shadow-md); margin-bottom: 20px; position: relative; background: linear-gradient(45deg, var(--primary-da), var(--primary-ml), var(--primary-cv)); padding: 4px; } .profile-pic img { border-radius: 50%; width: 100%; height: 100%; object-fit: cover; } .name-text { font-size: 2.6rem; font-weight: 700; margin-top: 10px; margin-bottom: 10px; } @keyframes float { 0% { transform: translateY(0px) } 50% { transform: translateY(-10px) } 100% { transform: translateY(0px) } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .about-text { max-width: 800px; margin: 0 auto 40px; font-size: 1.25rem; line-height: 1.6; color: var(--text-secondary); } .skills-container { margin-top: 20px; display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-bottom: 40px; } .skill-pill { background: rgba(255, 255, 255, 0.1); padding: 8px 16px; border-radius: 30px; font-size: 0.9rem; font-weight: 500; color: var(--text-secondary); } .social-links { display: flex; justify-content: center; gap: 20px; margin: 30px 0; } .social-button { background: rgba(255, 255, 255, 0.1); border: none; border-radius: 50%; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; transition: all var(--transition-med); color: var(--text-primary); font-size: 1.2rem; box-shadow: var(--shadow-sm); } .social-button:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.2); box-shadow: var(--shadow-md); } .social-linkedin:hover { background: #0077b5; } .social-github:hover { background: #333; } .social-email:hover { background: #ea4335; } .social-button svg { width: 24px; height: 24px; } /* Card styling */ .cards-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 30px; margin: 40px 0; } .card-container { position: relative; /* Important for button positioning */ margin-bottom: 20px; height: 100%; } .card-container.da:before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 6px; background: linear-gradient(90deg, var(--primary-da), var(--secondary-da)); z-index: 5; border-radius: var(--border-radius-md) var(--border-radius-md) 0 0; } .card-container.ml:before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 6px; background: linear-gradient(90deg, var(--primary-ml), var(--secondary-ml)); z-index: 5; transition: all var(--transition-med); border-radius: var(--border-radius-md) var(--border-radius-md) 0 0; } .card-container.cv:before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 6px; background: linear-gradient(90deg, var(--primary-cv), var(--secondary-cv)); z-index: 5; border-radius: var(--border-radius-md) var(--border-radius-md) 0 0; } .card-content { padding: 30px; min-height: 200px; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 26px; font-weight: 700; position: relative; z-index: 2; transition: all var(--transition-med); } .card-content svg { width: 60px; height: 60px; margin-bottom: 20px; opacity: 0.9; transition: all var(--transition-med); } .card-inner { transition: transform var(--transition-med), box-shadow var(--transition-med), background-color var(--transition-med); text-align: center; border-radius: var(--border-radius-md); background: var(--card-bg); overflow: hidden; box-shadow: var(--shadow-md); height: 100%; cursor: pointer; /* Indicates the card is clickable */ position: relative; /* Ensure child elements are positioned relative to the card */ } .card-inner:hover { transform: translateY(-10px) scale(1.05); /* Adds a slight zoom effect */ box-shadow: var(--shadow-lg); /* Increases shadow for emphasis */ background: rgba(255, 255, 255, 0.1); /* Subtle background change */ border: 2px solid var(--primary-da); /* Optional: Add a border to emphasize hover */ } .card-inner:hover .card-content svg { transform: scale(1.1); /* Slightly enlarges the icon */ opacity: 1; } .card-inner:hover .card-description { color: var(--text-primary); /* Optional: changes text color for emphasis */ } /* Add a subtle glow effect */ .card-inner:hover:before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border-radius: var(--border-radius-md); box-shadow: 0 0 15px rgba(255, 255, 255, 0.3); z-index: -1; } .card-description { padding: 0 20px 20px; color: var(--text-secondary); font-size: 1.1rem; line-height: 1.5; } /* Card button styling - crucial for making cards clickable */ .card-button { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; opacity: 0 !important; z-index: 10 !important; cursor: pointer !important; margin: 0 !important; padding: 0 !important; border: none !important; transform: scale(1.05) !important; transition: transform 0.2s ease !important; background: none !important; } /* Section styling */ .section-container { padding: 40px 20px; position: relative; } .section-container:before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 300px; background: radial-gradient(ellipse at top, rgba(255,255,255,0.05) 0%, transparent 70%); z-index: 0; } .da-section h1.section-heading { color: var(--primary-da); position: relative; display: inline-block; } .ml-section h1.section-heading { color: var(--primary-ml); position: relative; display: inline-block; } .cv-section h1.section-heading { color: var(--primary-cv); position: relative; display: inline-block; } .section-heading:after { content: ''; position: absolute; bottom: -10px; left: 0; width: 100%; height: 3px; border-radius: 3px; } .da-section .section-heading:after { background: var(--primary-da); } .ml-section .section-heading:after { background: var(--primary-ml); } .cv-section .section-heading:after { background: var(--primary-cv); } /* Subheadings color-coded */ .section-subheading.da { color: var(--primary-da); } .section-subheading.ml { color: var(--primary-ml); } .section-subheading.cv { color: var(--primary-cv); } /* Back buttons */ .back-button { border: none; border-radius: var(--border-radius-lg); padding: 10px 20px; font-size: 0.95rem; font-weight: 600; cursor: pointer; transition: transform var(--transition-fast), box-shadow var(--transition-fast); margin-bottom: 30px; display: flex; align-items: center; gap: 8px; } .back-button:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); } .back-button-da { background: linear-gradient(45deg, var(--primary-da), var(--secondary-da)); color: #fff; } .back-button-ml { background: linear-gradient(45deg, var(--primary-ml), var(--secondary-ml)); color: #fff; } .back-button-cv { background: linear-gradient(45deg, var(--primary-cv), var(--secondary-cv)); color: #fff; } .back-button svg { width: 20px; height: 20px; } /* Contact form */ .contact-container { background: var(--card-bg); border-radius: var(--border-radius-md); padding: 30px; max-width: 600px; margin: 0 auto; box-shadow: var(--shadow-md); } .hire-me-button { background: linear-gradient(45deg, var(--primary-da), var(--primary-cv)); color: white; border: none; border-radius: var(--border-radius-lg); padding: 12px 25px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all var(--transition-med); margin-top: 20px; box-shadow: var(--shadow-sm); display: inline-block; text-decoration: none; } .hire-me-button:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); filter: brightness(1.1); } /* Project cards */ .project-card { background: var(--card-bg); border-radius: var(--border-radius-md); padding: 25px; margin-bottom: 20px; box-shadow: var(--shadow-sm); transition: all var(--transition-med); border-left: 4px solid transparent; } .da-section .project-card { border-left-color: var(--primary-da); } .ml-section .project-card { border-left-color: var(--primary-ml); } .cv-section .project-card { border-left-color: var(--primary-cv); } .project-card:hover { transform: translateX(5px); box-shadow: var(--shadow-md); } .project-title { font-size: 1.3rem; font-weight: 600; margin-bottom: 10px; display: flex; align-items: center; justify-content: space-between; } .project-title-text { flex: 1; } .project-link { color: var(--text-secondary); transition: all var(--transition-med); text-decoration: none; display: inline-flex; align-items: center; margin-left: 10px; } .project-link svg { width: 16px; height: 16px; margin-right: 5px; } .da-section .project-title-text { color: var(--primary-da); } .ml-section .project-title-text { color: var(--primary-ml); } .cv-section .project-title-text { color: var(--primary-cv); } .da-section .project-link:hover { color: var(--primary-da); } .ml-section .project-link:hover { color: var(--primary-ml); } .cv-section .project-link:hover { color: var(--primary-cv); } .project-description { color: var(--text-secondary); line-height: 1.5; } .tech-stack { display: block; margin-top: 10px; font-style: italic; color: var(--text-muted); } /* Skills list */ .skills-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 15px; margin-top: 20px; margin-bottom: 40px; /* Added margin to create space between skills and projects */ } .skill-category { background: rgba(255, 255, 255, 0.05); border-radius: var(--border-radius-sm); padding: 15px; transition: all var(--transition-med); } .skill-category:hover { background: rgba(255, 255, 255, 0.08); transform: translateY(-3px); } .skill-category h4 { margin-top: 0; margin-bottom: 10px; font-size: 1.1rem; } .da-section .skill-category h4 { color: var(--primary-da); } .ml-section .skill-category h4 { color: var(--primary-ml); } .cv-section .skill-category h4 { color: var(--primary-cv); } .skill-category ul { margin: 0; padding-left: 20px; color: var(--text-secondary); } .skill-category li { margin-bottom: 5px; } /* Section intro text */ .section-intro { max-width: 800px; margin-bottom: 30px; line-height: 1.6; color: var(--text-secondary); font-size: 1.1rem; } /* Footer */ .footer { text-align: center; padding: 40px 20px; margin-top: 60px; color: var(--text-muted); font-size: 0.9rem; } /* Animations for scroll */ .animate-on-scroll { opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; } .animate-on-scroll.show { opacity: 1; transform: translateY(0); } /* Responsive design */ @media (max-width: 768px) { .landing-section h1 { font-size: 2.5rem; } .landing-section h2 { font-size: 1.5rem; } .about-text { font-size: 1.1rem; } .cards-grid { grid-template-columns: 1fr; } .skills-list { grid-template-columns: 1fr; } .profile-pic { width: 150px; height: 150px; } } @media (max-width: 480px) { .landing-section h1 { font-size: 2rem; } .landing-section h2 { font-size: 1.2rem; } .card-content { min-height: 150px; font-size: 22px; } .social-links { gap: 15px; } .social-button { width: 40px; height: 40px; font-size: 1rem; } } """ # --- Portfolio Layout --- with gr.Blocks(title="Manyue's Portfolio", css=portfolio_css) as demo: # Create sections # Data Analytics Section (initially hidden) with gr.Row(visible=False, elem_classes="section-container da-section") as da_section: with gr.Column(): # Back button back_from_da = gr.Button("← Back to Home", elem_classes="back-button back-button-da") gr.HTML("""
Looking for a data scientist or ML engineer for your team?
Hire Me