CoolFace
Apppublic

Tavashi/SkinSense_AI

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
streamlit_app.py547 linesDownload Raw Back to src
1import streamlit as st2import time3 4# ── Page config ────────────────────────────────────────────────5st.set_page_config(6    page_title="SkinSense AI",7    page_icon="🌸",8    layout="centered",9    initial_sidebar_state="collapsed"10)11 12# ── Global CSS ─────────────────────────────────────────────────13st.markdown("""14<style>15@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:wght@300;400;500;600&display=swap');16 17/* ── Reset & base ── */18*, *::before, *::after { box-sizing: border-box; }19 20html, body, .stApp {21    background: #FDF4F5 !important;22    font-family: 'DM Sans', sans-serif;23}24 25/* Hide Streamlit chrome */26#MainMenu, footer, header { visibility: hidden; }27.block-container {28    padding: 2rem 1.5rem 4rem !important;29    max-width: 720px !important;30}31 32/* ── Hero ── */33.hero {34    text-align: center;35    padding: 3rem 1rem 2rem;36}37.hero-eyebrow {38    font-size: 0.72rem;39    font-weight: 600;40    letter-spacing: 0.18em;41    color: #C9748A;42    text-transform: uppercase;43    margin-bottom: 0.8rem;44}45.hero-title {46    font-family: 'DM Serif Display', serif;47    font-size: 3rem;48    line-height: 1.1;49    color: #2B1A20;50    margin: 0 0 1rem;51}52.hero-title em {53    font-style: italic;54    color: #C9748A;55}56.hero-sub {57    font-size: 1rem;58    color: #7A5563;59    font-weight: 300;60    max-width: 420px;61    margin: 0 auto;62    line-height: 1.6;63    text-align: center !important;64}65 66/* ── Divider ── */67.soft-divider {68    height: 1px;69    background: linear-gradient(to right, transparent, #F0C4CE, transparent);70    margin: 2rem 0;71}72 73/* ── Section label ── */74.section-label {75    font-size: 0.7rem;76    font-weight: 600;77    letter-spacing: 0.15em;78    text-transform: uppercase;79    color: #C9748A;80    margin-bottom: 1.2rem;81}82 83/* ── Pill selectors ── */84.pill-group {85    display: flex;86    flex-wrap: wrap;87    gap: 0.5rem;88    margin-bottom: 0.5rem;89}90.pill {91    display: inline-block;92    padding: 0.45rem 1rem;93    border-radius: 999px;94    font-size: 0.85rem;95    font-weight: 500;96    cursor: pointer;97    border: 1.5px solid #F0C4CE;98    background: #FFFFFF;99    color: #7A5563;100    transition: all 0.15s ease;101    user-select: none;102}103.pill:hover { border-color: #C9748A; color: #C9748A; }104.pill.active {105    background: #C9748A;106    border-color: #C9748A;107    color: #FFFFFF;108}109 110/* ── Question block ── */111 112/* ── Generate button ── */113.stButton > button {114    width: 100%;115    background: linear-gradient(135deg, #C9748A 0%, #E8A0B0 100%) !important;116    color: white !important;117    border: none !important;118    border-radius: 14px !important;119    padding: 0.85rem 2rem !important;120    font-family: 'DM Sans', sans-serif !important;121    font-size: 1rem !important;122    font-weight: 600 !important;123    letter-spacing: 0.03em !important;124    cursor: pointer !important;125    margin-top: 1rem !important;126    transition: opacity 0.2s !important;127}128.stButton > button:hover { opacity: 0.88 !important; }129 130/* ── Routine cards ── */131.routine-card {132    background: #FFFFFF;133    border: 1px solid #F5DDE2;134    border-radius: 18px;135    padding: 1.4rem 1.6rem;136    margin-bottom: 0.85rem;137    display: flex;138    gap: 1.1rem;139    align-items: flex-start;140}141.card-step {142    min-width: 36px;143    height: 36px;144    border-radius: 50%;145    background: linear-gradient(135deg, #C9748A, #E8A0B0);146    color: white;147    font-size: 0.8rem;148    font-weight: 700;149    display: flex;150    align-items: center;151    justify-content: center;152    flex-shrink: 0;153    margin-top: 2px;154}155.card-body {}156.card-type {157    font-size: 0.7rem;158    font-weight: 600;159    letter-spacing: 0.12em;160    text-transform: uppercase;161    color: #C9748A;162    margin-bottom: 0.2rem;163}164.card-product {165    font-family: 'DM Serif Display', serif;166    font-size: 1.15rem;167    color: #2B1A20;168    margin-bottom: 0.3rem;169    line-height: 1.25;170}171.card-why {172    font-size: 0.82rem;173    color: #7A5563;174    line-height: 1.5;175    font-weight: 300;176}177.card-timing {178    display: inline-block;179    margin-top: 0.55rem;180    font-size: 0.7rem;181    font-weight: 600;182    letter-spacing: 0.08em;183    background: #FDF4F5;184    border: 1px solid #F0C4CE;185    color: #C9748A;186    border-radius: 999px;187    padding: 0.2rem 0.65rem;188}189 190/* ── Tips block ── */191.tips-block {192    background: linear-gradient(135deg, #FDF0F3, #FDE8EE);193    border: 1px solid #F0C4CE;194    border-radius: 16px;195    padding: 1.3rem 1.5rem;196    margin-top: 1.5rem;197}198.tips-title {199    font-size: 0.75rem;200    font-weight: 600;201    letter-spacing: 0.14em;202    text-transform: uppercase;203    color: #C9748A;204    margin-bottom: 0.8rem;205}206.tip-item {207    font-size: 0.85rem;208    color: #5A3540;209    padding: 0.35rem 0;210    border-bottom: 1px solid #F5DDE2;211    line-height: 1.5;212}213.tip-item:last-child { border-bottom: none; }214 215/* ── Streamlit widget overrides ── */216div[data-testid="stSelectbox"] > div > div {217    background: #FFFFFF !important;218    border: 1.5px solid #F0C4CE !important;219    border-radius: 12px !important;220    color: #2B1A20 !important;221    font-family: 'DM Sans', sans-serif !important;222}223div[data-testid="stMultiSelect"] > div > div {224    background: #FFFFFF !important;225    border: 1.5px solid #F0C4CE !important;226    border-radius: 12px !important;227}228div[data-testid="stMultiSelect"] span[data-baseweb="tag"] {229    background-color: #C9748A !important;230    border-radius: 999px !important;231}232label[data-testid="stWidgetLabel"] p {233    font-family: 'DM Sans', sans-serif !important;234    font-size: 0.95rem !important;235    font-weight: 600 !important;236    color: #2B1A20 !important;237}238</style>239""", unsafe_allow_html=True)240 241 242# ── Recommendation data ────────────────────────────────────────243 244CLEANSER_MAP = {245    "Very Dry": {246        "product": "Cream-to-Milk Gentle Cleanser",247        "ingredient": "Ceramide NP + Glycerin",248        "why": "Low-foam formula preserves the skin barrier — won't strip moisture that dry skin can't afford to lose.",249        "timing": "AM + PM"250    },251    "Dry": {252        "product": "Soothing Milky Cleanser",253        "ingredient": "Oat Extract + Allantoin",254        "why": "Allantoin calms dry, flaky skin while cleansing gently enough for twice-daily use.",255        "timing": "AM + PM"256    },257    "Balanced": {258        "product": "Gentle Daily Gel Wash",259        "ingredient": "Green Tea Extract + Panthenol",260        "why": "Mild surfactants clean without over-stripping — ideal for skin that doesn't need aggressive correction.",261        "timing": "AM + PM"262    },263    "Oily": {264        "product": "Pore-Clearing Foam Cleanser",265        "ingredient": "Salicylic Acid 0.5% + Zinc",266        "why": "BHA at low dose dissolves excess sebum inside pores without triggering rebound oiliness.",267        "timing": "AM + PM"268    },269    "Very Oily": {270        "product": "Deep-Clean Balancing Wash",271        "ingredient": "Salicylic Acid 2% + Niacinamide",272        "why": "2% BHA tackles congestion at the source. Niacinamide regulates sebum production over time.",273        "timing": "AM + PM"274    },275}276 277SERUM_MAP = {278    "Pimples": {279        "product": "Blemish Control Serum",280        "ingredient": "Niacinamide 10% + Zinc PCA",281        "why": "Niacinamide reduces inflammation and sebum production. Zinc PCA accelerates healing without dryness.",282        "timing": "PM"283    },284    "Cysts": {285        "product": "Retinol Renewal Serum",286        "ingredient": "Encapsulated Retinol 0.3%",287        "why": "Retinol speeds cell turnover to prevent pore blockages that cause deep cystic breakouts.",288        "timing": "PM only"289    },290    "Blackheads": {291        "product": "Pore-Refining Exfoliant",292        "ingredient": "BHA (Salicylic Acid 2%) + Witch Hazel",293        "why": "Oil-soluble BHA reaches inside pores to dislodge oxidized sebum — the root cause of blackheads.",294        "timing": "PM"295    },296    "Whiteheads": {297        "product": "Clarifying Treatment Serum",298        "ingredient": "Benzoyl Peroxide 2.5% + Allantoin",299        "why": "Low-dose BPO clears closed comedones without the peeling that higher concentrations cause.",300        "timing": "PM"301    },302    "Milia": {303        "product": "Gentle AHA Resurfacing Serum",304        "ingredient": "Glycolic Acid 5% + Lactic Acid 3%",305        "why": "Dual AHA blend softens keratin plugs that form milia. Start 2–3x weekly before daily use.",306        "timing": "PM"307    },308    "default": {309        "product": "Radiance Brightening Serum",310        "ingredient": "Vitamin C (THD Ascorbate) 10% + Ferulic Acid",311        "why": "Stable Vitamin C form boosts collagen and brightens evenly. Ferulic acid doubles its antioxidant life.",312        "timing": "AM"313    },314}315 316MOISTURIZER_MAP = {317    "Very Dry": {318        "product": "Barrier Repair Rich Cream",319        "ingredient": "Ceramide Complex + Shea Butter + Squalane",320        "why": "Triple lipid blend rebuilds the skin barrier and seals in moisture for 24-hour hydration.",321        "timing": "AM + PM"322    },323    "Dry": {324        "product": "Hydrating Day Cream",325        "ingredient": "Hyaluronic Acid (3 weights) + Glycerin",326        "why": "Multi-weight HA draws water into all skin layers. Glycerin holds it there all day.",327        "timing": "AM + PM"328    },329    "Balanced": {330        "product": "Lightweight Gel-Cream",331        "ingredient": "Aloe Vera + Beta-Glucan",332        "why": "Beta-glucan provides barrier support without heaviness. Perfect maintenance for skin that's already balanced.",333        "timing": "AM + PM"334    },335    "Oily": {336        "product": "Oil-Free Pore-Minimizing Gel",337        "ingredient": "Niacinamide 5% + Zinc + Dimethicone",338        "why": "Water-based texture hydrates without adding shine. Niacinamide tightens pores with consistent use.",339        "timing": "AM + PM"340    },341    "Very Oily": {342        "product": "Mattifying Water Gel",343        "ingredient": "Sebum-Control Complex + Kaolin",344        "why": "Kaolin absorbs excess oil throughout the day. Zero comedogenic ingredients — won't clog pores.",345        "timing": "AM + PM"346    },347}348 349SUNSCREEN_MAP = {350    "Minimal": {351        "product": "Everyday Tinted SPF 30",352        "ingredient": "Zinc Oxide + Iron Oxides",353        "why": "Mineral filter protects against UV and visible light. Tint evens skintone without foundation.",354        "timing": "AM only"355    },356    "Moderate": {357        "product": "Hydrating Broad-Spectrum SPF 50",358        "ingredient": "Tinosorb S + Uvinul A Plus + Hyaluronic Acid",359        "why": "Next-gen EU filters offer superior UVA protection vs. older US formulas. Added HA means no separate moisturizer needed.",360        "timing": "AM only — reapply every 2 hrs outdoors"361    },362    "High": {363        "product": "Sport Shield SPF 50+ PA++++",364        "ingredient": "Tinosorb M + Tinosorb S + Antioxidant Complex",365        "why": "PA++++ is the highest UVA rating. Antioxidants neutralize free radicals UV filters can't block.",366        "timing": "AM — reapply every 90 min"367    },368    "Very High": {369        "product": "Extreme Protection SPF 100+",370        "ingredient": "Mexoryl XL + Mexoryl SX + Zinc Oxide",371        "why": "Triple-filter system for maximum coverage. Required for prolonged direct sun — sports, beach, outdoor work.",372        "timing": "AM — reapply every hour"373    },374}375 376EXTRA_MAP = {377    "pore": {378        "product": "Pore-Refining Clay Mask",379        "ingredient": "Kaolin + Bentonite + Niacinamide",380        "why": "Use 2x weekly to deep-clean pores and tighten their appearance over time.",381        "timing": "2–3x weekly (PM)"382    },383    "acne": {384        "product": "Overnight Spot Patch Treatment",385        "ingredient": "Hydrocolloid + Tea Tree + Centella Asiatica",386        "why": "Hydrocolloid draws out impurities while you sleep. Centella calms redness by morning.",387        "timing": "PM (spot use)"388    },389    "default": {390        "product": "Brightening Eye Cream",391        "ingredient": "Caffeine + Peptides + Vitamin K",392        "why": "Caffeine drains puffiness. Peptides firm the delicate under-eye area with regular use.",393        "timing": "AM + PM"394    },395}396 397 398def build_routine(oiliness, concerns, pore, sun):399    steps = []400 401    # Step 1: Cleanser402    c = CLEANSER_MAP[oiliness]403    steps.append({"type": "Cleanser", **c})404 405    # Step 2: Serum — pick based on first concern, fallback to default406    if concerns:407        s = SERUM_MAP.get(concerns[0], SERUM_MAP["default"])408    else:409        s = SERUM_MAP["default"]410    steps.append({"type": "Treatment Serum", **s})411 412    # Step 3: Moisturizer413    m = MOISTURIZER_MAP[oiliness]414    steps.append({"type": "Moisturizer", **m})415 416    # Step 4: Sunscreen417    sp = SUNSCREEN_MAP[sun]418    steps.append({"type": "Sunscreen", **sp})419 420    # Step 5: Targeted extra421    if pore == "Large & Clogged":422        e = EXTRA_MAP["pore"]423    elif concerns and any(c in concerns for c in ["Pimples", "Cysts"]):424        e = EXTRA_MAP["acne"]425    else:426        e = EXTRA_MAP["default"]427    steps.append({"type": "Targeted Treatment", **e})428 429    return steps430 431 432def build_tips(oiliness, concerns, zones, sun):433    tips = []434    if oiliness in ["Oily", "Very Oily"]:435        tips.append("Use lukewarm — not hot — water. Heat triggers more sebum production.")436    if oiliness in ["Dry", "Very Dry"]:437        tips.append("Apply moisturizer within 60 seconds of washing while skin is still slightly damp.")438    if len(zones) >= 3:439        tips.append("Combination skin: use a richer moisturizer on dry zones, gel on oily zones.")440    if "Cysts" in concerns or "Pimples" in concerns:441        tips.append("Actives take 6–8 weeks to show results. Consistency beats switching products.")442    if sun in ["High", "Very High"]:443        tips.append("UV breaks down SPF. Reapply every 90–120 minutes when outdoors.")444    if not tips:445        tips.append("Patch test new actives on your jaw for 48 hours before applying to full face.")446    return tips447 448 449# ── App layout ─────────────────────────────────────────────────450 451# Hero452st.markdown("""453<div class="hero">454    <div class="hero-eyebrow">Skin Intelligence</div>455    <h1 class="hero-title"><em>Your skin,</em><br><em>finally understood.</em></h1>456 457</div>458""", unsafe_allow_html=True)459 460st.markdown("<div class='soft-divider'></div>", unsafe_allow_html=True)461 462# ── Inputs ─────────────────────────────────────────────────────463st.markdown("<div class='section-label'>Tell us about your skin</div>", unsafe_allow_html=True)464 465# Q1 — Oiliness466oiliness_options = ["Very Dry", "Dry", "Balanced", "Oily", "Very Oily"]467selected_oiliness = st.selectbox(468    "How does your skin feel by midday?",469    options=oiliness_options,470    index=2,471    help="Think about an average day without any products."472)473 474# Q2 — Zone Mapping475zone_options = ["T-zone", "Cheeks", "Forehead", "Chin"]476selected_zones = st.multiselect(477    "Which zones behave differently from the rest?",478    options=zone_options,479    default=[],480    help="Select all that apply. Leave blank if your skin is consistent."481)482 483# Q3 — Active Concerns484concern_options = ["Pimples", "Cysts", "Blackheads", "Whiteheads", "Milia"]485selected_concerns = st.multiselect(486    "Any active concerns right now?",487    options=concern_options,488    default=[],489    help="Select what's present on your skin currently."490)491 492# Q4 — Pore Appearance493pore_options = ["Invisible", "Noticeable", "Large & Clogged"]494selected_pore = st.selectbox(495    "How would you describe your pores?",496    options=pore_options,497    index=1498)499 500# Q5 — Sun Exposure501sun_options = ["Minimal", "Moderate", "High", "Very High"]502selected_sun = st.selectbox(503    "How much sun do you typically get?",504    options=sun_options,505    index=1,506    help="Minimal = mostly indoors. Very High = outdoors most of the day."507)508 509st.markdown("<div class='soft-divider'></div>", unsafe_allow_html=True)510 511# ── Generate button ─────────────────────────────────────────────512if st.button("Build My Routine ✦"):513    with st.spinner("Analysing your skin profile..."):514        time.sleep(1.4)515 516    routine = build_routine(selected_oiliness, selected_concerns, selected_pore, selected_sun)517    tips = build_tips(selected_oiliness, selected_concerns, selected_zones, selected_sun)518 519    st.markdown("<div class='section-label' style='margin-top:0.5rem;'>Your Personalized Routine</div>", unsafe_allow_html=True)520 521    for i, step in enumerate(routine, 1):522        st.markdown(f"""523        <div class="routine-card">524            <div class="card-step">{i:02d}</div>525            <div class="card-body">526                <div class="card-type">{step['type']}</div>527                <div class="card-product">{step['product']}</div>528                <div class="card-why">Key ingredient: <strong>{step['ingredient']}</strong><br>{step['why']}</div>529                <span class="card-timing">⏱ {step['timing']}</span>530            </div>531        </div>532        """, unsafe_allow_html=True)533 534    # Tips535    tips_html = "".join([f"<div class='tip-item'>→ {t}</div>" for t in tips])536    st.markdown(f"""537    <div class="tips-block">538        <div class="tips-title">💡 Quick Wins for Your Skin Type</div>539        {tips_html}540    </div>541    """, unsafe_allow_html=True)542 543    st.markdown("<div class='soft-divider'></div>", unsafe_allow_html=True)544    st.markdown("<p style='text-align:center; color:#B08090; font-size:0.8rem;'>Results are based on your inputs. Always patch-test new products.</p>", unsafe_allow_html=True)545 546# Footer547st.markdown("<p style='text-align:center; color:#D4A0B0; font-size:0.75rem; margin-top:3rem;'>SkinSense AI • Powered by Streamlit</p>", unsafe_allow_html=True)