From 598c33da70da0034d90d2afe9dbf336adb3809ad Mon Sep 17 00:00:00 2001 From: Eithan Malonzo Date: Mon, 23 Mar 2026 14:42:50 +0800 Subject: [PATCH 1/2] feat: add grid and list view toggle for projects Resolves #8 --- static/css/style.css | 980 ++++++++++++++++++++++++------------------- templates/base.html | 36 +- templates/index.html | 429 ++++++++++++------- 3 files changed, 834 insertions(+), 611 deletions(-) diff --git a/static/css/style.css b/static/css/style.css index f24de47..900cf83 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,674 +1,768 @@ :root { - --cream: #F6F1EB; - --cream-dark: #EDE6DC; - --indigo: #1B1464; - --indigo-light: #2D2387; - --gold: #D4952A; - --gold-light: #F0BE5E; - --terracotta: #C75B39; - --sage: #5A7A64; - --text-dark: #1a1a1a; - --text-mid: #4a4a4a; - --text-light: #7a7a7a; - --card-shadow: 0 2px 20px rgba(27, 20, 100, 0.06); - --card-shadow-hover: 0 12px 40px rgba(27, 20, 100, 0.12); + --cream: #f6f1eb; + --cream-dark: #ede6dc; + --indigo: #1b1464; + --indigo-light: #2d2387; + --gold: #d4952a; + --gold-light: #f0be5e; + --terracotta: #c75b39; + --sage: #5a7a64; + --text-dark: #1a1a1a; + --text-mid: #4a4a4a; + --text-light: #7a7a7a; + --card-shadow: 0 2px 20px rgba(27, 20, 100, 0.06); + --card-shadow-hover: 0 12px 40px rgba(27, 20, 100, 0.12); } * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } body { - font-family: 'DM Sans', sans-serif; - background: var(--cream); - color: var(--text-dark); - line-height: 1.6; - overflow-x: hidden; + font-family: "DM Sans", sans-serif; + background: var(--cream); + color: var(--text-dark); + line-height: 1.6; + overflow-x: hidden; } body::after { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - opacity: 0.025; - z-index: 9999; - background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); - background-repeat: repeat; - background-size: 256px 256px; + content: ""; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + opacity: 0.025; + z-index: 9999; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); + background-repeat: repeat; + background-size: 256px 256px; } nav { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 100; - padding: 1.25rem 2.5rem; - display: flex; - align-items: center; - justify-content: space-between; - background: rgba(246, 241, 235, 0.85); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - border-bottom: 1px solid rgba(27, 20, 100, 0.06); - transition: box-shadow 0.3s ease; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + padding: 1.25rem 2.5rem; + display: flex; + align-items: center; + justify-content: space-between; + background: rgba(246, 241, 235, 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid rgba(27, 20, 100, 0.06); + transition: box-shadow 0.3s ease; } nav.scrolled { - box-shadow: 0 2px 20px rgba(27, 20, 100, 0.08); + box-shadow: 0 2px 20px rgba(27, 20, 100, 0.08); } .nav-brand { - font-family: 'Instrument Serif', serif; - font-size: 1.5rem; - color: var(--indigo); - text-decoration: none; - letter-spacing: -0.02em; + font-family: "Instrument Serif", serif; + font-size: 1.5rem; + color: var(--indigo); + text-decoration: none; + letter-spacing: -0.02em; } .nav-brand span { - color: var(--gold); + color: var(--gold); } .nav-links { - display: flex; - gap: 2rem; - align-items: center; + display: flex; + gap: 2rem; + align-items: center; } .nav-links a { - font-size: 0.85rem; - font-weight: 500; - color: var(--text-mid); - text-decoration: none; - letter-spacing: 0.04em; - text-transform: uppercase; - transition: color 0.2s ease; + font-size: 0.85rem; + font-weight: 500; + color: var(--text-mid); + text-decoration: none; + letter-spacing: 0.04em; + text-transform: uppercase; + transition: color 0.2s ease; } .nav-links a:hover { - color: var(--indigo); + color: var(--indigo); } .nav-cta { - background: var(--indigo) !important; - color: var(--cream) !important; - padding: 0.55rem 1.25rem; - border-radius: 100px; - font-size: 0.8rem !important; - letter-spacing: 0.05em; - transition: background 0.2s ease, transform 0.2s ease !important; + background: var(--indigo) !important; + color: var(--cream) !important; + padding: 0.55rem 1.25rem; + border-radius: 100px; + font-size: 0.8rem !important; + letter-spacing: 0.05em; + transition: + background 0.2s ease, + transform 0.2s ease !important; } .nav-cta:hover { - background: var(--indigo-light) !important; - transform: translateY(-1px); + background: var(--indigo-light) !important; + transform: translateY(-1px); } .hero { - padding: 10rem 2.5rem 6rem; - max-width: 1300px; - margin: 0 auto; - position: relative; + padding: 10rem 2.5rem 6rem; + max-width: 1300px; + margin: 0 auto; + position: relative; } .hero-label { - display: inline-block; - font-size: 0.75rem; - font-weight: 600; - letter-spacing: 0.12em; - text-transform: uppercase; - color: var(--gold); - margin-bottom: 1.5rem; - opacity: 0; - animation: fadeUp 0.6s ease forwards; - animation-delay: 0.1s; + display: inline-block; + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--gold); + margin-bottom: 1.5rem; + opacity: 0; + animation: fadeUp 0.6s ease forwards; + animation-delay: 0.1s; } .hero h1 { - font-family: 'Instrument Serif', serif; - font-size: clamp(3.2rem, 7vw, 6.5rem); - line-height: 1.05; - letter-spacing: -0.03em; - color: var(--indigo); - max-width: 900px; - opacity: 0; - animation: fadeUp 0.8s ease forwards; - animation-delay: 0.2s; + font-family: "Instrument Serif", serif; + font-size: clamp(3.2rem, 7vw, 6.5rem); + line-height: 1.05; + letter-spacing: -0.03em; + color: var(--indigo); + max-width: 900px; + opacity: 0; + animation: fadeUp 0.8s ease forwards; + animation-delay: 0.2s; } .hero h1 em { - font-style: italic; - color: var(--terracotta); + font-style: italic; + color: var(--terracotta); } .hero-description { - margin-top: 2rem; - font-size: 1.15rem; - color: var(--text-mid); - max-width: 520px; - line-height: 1.7; - opacity: 0; - animation: fadeUp 0.8s ease forwards; - animation-delay: 0.4s; + margin-top: 2rem; + font-size: 1.15rem; + color: var(--text-mid); + max-width: 520px; + line-height: 1.7; + opacity: 0; + animation: fadeUp 0.8s ease forwards; + animation-delay: 0.4s; } .hero-decoration { - position: absolute; - right: 2.5rem; - top: 12rem; - width: 280px; - height: 280px; - opacity: 0; - animation: fadeIn 1.2s ease forwards; - animation-delay: 0.6s; + position: absolute; + right: 2.5rem; + top: 12rem; + width: 280px; + height: 280px; + opacity: 0; + animation: fadeIn 1.2s ease forwards; + animation-delay: 0.6s; } .hero-decoration .circle { - position: absolute; - border-radius: 50%; + position: absolute; + border-radius: 50%; } .hero-decoration .circle-1 { - width: 280px; - height: 280px; - background: linear-gradient(135deg, var(--gold-light) 0%, var(--gold) 100%); - opacity: 0.15; - top: 0; - left: 0; + width: 280px; + height: 280px; + background: linear-gradient(135deg, var(--gold-light) 0%, var(--gold) 100%); + opacity: 0.15; + top: 0; + left: 0; } .hero-decoration .circle-2 { - width: 180px; - height: 180px; - background: linear-gradient(135deg, var(--indigo) 0%, var(--indigo-light) 100%); - opacity: 0.1; - top: 120px; - left: -60px; + width: 180px; + height: 180px; + background: linear-gradient( + 135deg, + var(--indigo) 0%, + var(--indigo-light) 100% + ); + opacity: 0.1; + top: 120px; + left: -60px; } .hero-decoration .circle-3 { - width: 100px; - height: 100px; - background: var(--terracotta); - opacity: 0.12; - top: -20px; - left: 200px; + width: 100px; + height: 100px; + background: var(--terracotta); + opacity: 0.12; + top: -20px; + left: 200px; } .stats-bar { - max-width: 1300px; - margin: 0 auto 4rem; - padding: 0 2.5rem; - display: flex; - gap: 3rem; - opacity: 0; - animation: fadeUp 0.8s ease forwards; - animation-delay: 0.6s; + max-width: 1300px; + margin: 0 auto 4rem; + padding: 0 2.5rem; + display: flex; + gap: 3rem; + opacity: 0; + animation: fadeUp 0.8s ease forwards; + animation-delay: 0.6s; } .stat { - display: flex; - align-items: baseline; - gap: 0.5rem; + display: flex; + align-items: baseline; + gap: 0.5rem; } .stat-number { - font-family: 'Instrument Serif', serif; - font-size: 2.2rem; - color: var(--indigo); - letter-spacing: -0.02em; + font-family: "Instrument Serif", serif; + font-size: 2.2rem; + color: var(--indigo); + letter-spacing: -0.02em; } .stat-label { - font-size: 0.8rem; - color: var(--text-light); - text-transform: uppercase; - letter-spacing: 0.06em; - font-weight: 500; + font-size: 0.8rem; + color: var(--text-light); + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 500; } .stat-divider { - width: 1px; - height: 32px; - background: rgba(27, 20, 100, 0.1); - align-self: center; + width: 1px; + height: 32px; + background: rgba(27, 20, 100, 0.1); + align-self: center; } .section-heading { - max-width: 1300px; - margin: 0 auto; - padding: 0 2.5rem 3rem; - display: flex; - align-items: baseline; - justify-content: space-between; + max-width: 1300px; + margin: 0 auto; + padding: 0 2.5rem 3rem; + display: flex; + align-items: baseline; + justify-content: space-between; } .section-heading h2 { - font-family: 'Instrument Serif', serif; - font-size: 2.5rem; - color: var(--indigo); - letter-spacing: -0.02em; + font-family: "Instrument Serif", serif; + font-size: 2.5rem; + color: var(--indigo); + letter-spacing: -0.02em; } .section-heading .line { - flex: 1; - height: 1px; - background: rgba(27, 20, 100, 0.1); - margin-left: 2rem; + flex: 1; + height: 1px; + background: rgba(27, 20, 100, 0.1); + margin-left: 2rem; + margin-right: 2rem; } .projects { - max-width: 1300px; - margin: 0 auto; - padding: 0 2.5rem 6rem; + max-width: 1300px; + margin: 0 auto; + padding: 0 2.5rem 6rem; } .projects-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 1.5rem; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.5rem; } .project-card { - background: white; - border-radius: 16px; - padding: 2.5rem; - position: relative; - overflow: hidden; - border: 1px solid rgba(27, 20, 100, 0.04); - box-shadow: var(--card-shadow); - transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1), - box-shadow 0.4s cubic-bezier(0.23, 1, 0.32, 1); - opacity: 0; - animation: fadeUp 0.6s ease forwards; + background: white; + border-radius: 16px; + padding: 2.5rem; + position: relative; + overflow: hidden; + border: 1px solid rgba(27, 20, 100, 0.04); + box-shadow: var(--card-shadow); + transition: + transform 0.4s cubic-bezier(0.23, 1, 0.32, 1), + box-shadow 0.4s cubic-bezier(0.23, 1, 0.32, 1); + opacity: 0; + animation: fadeUp 0.6s ease forwards; } -.project-card:nth-child(1) { animation-delay: 0.3s; } -.project-card:nth-child(2) { animation-delay: 0.4s; } -.project-card:nth-child(3) { animation-delay: 0.5s; } -.project-card:nth-child(4) { animation-delay: 0.6s; } +.project-card:nth-child(1) { + animation-delay: 0.3s; +} +.project-card:nth-child(2) { + animation-delay: 0.4s; +} +.project-card:nth-child(3) { + animation-delay: 0.5s; +} +.project-card:nth-child(4) { + animation-delay: 0.6s; +} .project-card:hover { - transform: translateY(-4px); - box-shadow: var(--card-shadow-hover); + transform: translateY(-4px); + box-shadow: var(--card-shadow-hover); } .project-card::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - border-radius: 16px 16px 0 0; - transition: opacity 0.3s ease; + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + border-radius: 16px 16px 0 0; + transition: opacity 0.3s ease; } -.project-card:nth-child(1)::before { background: var(--gold); } -.project-card:nth-child(2)::before { background: var(--terracotta); } -.project-card:nth-child(3)::before { background: var(--sage); } -.project-card:nth-child(4)::before { background: var(--indigo); } +.project-card:nth-child(1)::before { + background: var(--gold); +} +.project-card:nth-child(2)::before { + background: var(--terracotta); +} +.project-card:nth-child(3)::before { + background: var(--sage); +} +.project-card:nth-child(4)::before { + background: var(--indigo); +} .project-card::before { - opacity: 0; + opacity: 0; } .project-card:hover::before { - opacity: 1; + opacity: 1; } .card-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - margin-bottom: 1.25rem; + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 1.25rem; } .project-number { - font-family: 'Instrument Serif', serif; - font-size: 0.9rem; - color: var(--text-light); - opacity: 0.5; + font-family: "Instrument Serif", serif; + font-size: 0.9rem; + color: var(--text-light); + opacity: 0.5; } .project-tag { - font-size: 0.65rem; - font-weight: 600; - letter-spacing: 0.1em; - text-transform: uppercase; - padding: 0.3rem 0.75rem; - border-radius: 100px; - background: var(--cream); + font-size: 0.65rem; + font-weight: 600; + letter-spacing: 0.1em; + text-transform: uppercase; + padding: 0.3rem 0.75rem; + border-radius: 100px; + background: var(--cream); } -.project-tag.framework { color: var(--gold); } -.project-tag.generator { color: var(--terracotta); } -.project-tag.tools { color: var(--sage); } -.project-tag.community { color: var(--indigo); } +.project-tag.framework { + color: var(--gold); +} +.project-tag.generator { + color: var(--terracotta); +} +.project-tag.tools { + color: var(--sage); +} +.project-tag.community { + color: var(--indigo); +} .project-name { - font-family: 'Instrument Serif', serif; - font-size: 2rem; - color: var(--indigo); - letter-spacing: -0.02em; - margin-bottom: 0.75rem; - line-height: 1.2; + font-family: "Instrument Serif", serif; + font-size: 2rem; + color: var(--indigo); + letter-spacing: -0.02em; + margin-bottom: 0.75rem; + line-height: 1.2; } .project-description { - font-size: 0.95rem; - color: var(--text-mid); - line-height: 1.7; - margin-bottom: 2rem; + font-size: 0.95rem; + color: var(--text-mid); + line-height: 1.7; + margin-bottom: 2rem; } .project-meta { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 1.75rem; + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 1.75rem; } .meta-item { - font-size: 0.78rem; - color: var(--text-light); - display: flex; - align-items: center; - gap: 0.35rem; + font-size: 0.78rem; + color: var(--text-light); + display: flex; + align-items: center; + gap: 0.35rem; } .meta-dot { - width: 3px; - height: 3px; - border-radius: 50%; - background: var(--text-light); - opacity: 0.4; + width: 3px; + height: 3px; + border-radius: 50%; + background: var(--text-light); + opacity: 0.4; } .project-links { - display: flex; - gap: 0.75rem; + display: flex; + gap: 0.75rem; } .project-link { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.6rem 1.2rem; - font-size: 0.8rem; - font-weight: 500; - text-decoration: none; - border-radius: 8px; - transition: all 0.25s ease; - letter-spacing: 0.02em; + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.6rem 1.2rem; + font-size: 0.8rem; + font-weight: 500; + text-decoration: none; + border-radius: 8px; + transition: all 0.25s ease; + letter-spacing: 0.02em; } .link-website { - background: var(--indigo); - color: var(--cream); + background: var(--indigo); + color: var(--cream); } .link-website:hover { - background: var(--indigo-light); - transform: translateY(-1px); + background: var(--indigo-light); + transform: translateY(-1px); } .link-github { - background: var(--cream); - color: var(--text-dark); - border: 1px solid rgba(27, 20, 100, 0.1); + background: var(--cream); + color: var(--text-dark); + border: 1px solid rgba(27, 20, 100, 0.1); } .link-github:hover { - background: var(--cream-dark); - border-color: rgba(27, 20, 100, 0.2); - transform: translateY(-1px); + background: var(--cream-dark); + border-color: rgba(27, 20, 100, 0.2); + transform: translateY(-1px); } .link-icon { - width: 16px; - height: 16px; - flex-shrink: 0; + width: 16px; + height: 16px; + flex-shrink: 0; +} + +/* List View Layout */ +.projects-list { + display: grid; + gap: 1.5rem; + grid-template-columns: repeat(1, 1fr); +} + +@media (min-width: 901px) { + .projects-list .project-card { + display: grid; + grid-template-columns: 100px 1fr auto; + grid-template-areas: + "header name links" + "header desc links" + "header meta links"; + column-gap: 2.5rem; + row-gap: 2.5em; + align-items: start; + padding: 2.5rem; + } + + .projects-list .card-header { + grid-area: header; + flex-direction: column; + align-items: flex-start; + gap: 1rem; + margin-bottom: 0; + } + + .projects-list .project-name { + grid-area: name; + margin-bottom: 0; + } + + .projects-list .project-description { + grid-area: desc; + margin-bottom: 0.5rem; + } + + .projects-list .project-meta { + grid-area: meta; + margin-bottom: 0; + } + + .projects-list .project-links { + grid-area: links; + flex-direction: column; + gap: 0.75rem; + align-self: center; + justify-content: center; + } } .contribute { - max-width: 1300px; - margin: 0 auto; - padding: 0 2.5rem 6rem; + max-width: 1300px; + margin: 0 auto; + padding: 0 2.5rem 6rem; } .contribute-card { - background: var(--indigo); - border-radius: 20px; - padding: 4rem 5rem; - display: flex; - align-items: center; - justify-content: space-between; - gap: 3rem; - position: relative; - overflow: hidden; + background: var(--indigo); + border-radius: 20px; + padding: 4rem 5rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 3rem; + position: relative; + overflow: hidden; } .contribute-card::before { - content: ''; - position: absolute; - top: -100px; - right: -100px; - width: 400px; - height: 400px; - border-radius: 50%; - background: var(--indigo-light); - opacity: 0.3; + content: ""; + position: absolute; + top: -100px; + right: -100px; + width: 400px; + height: 400px; + border-radius: 50%; + background: var(--indigo-light); + opacity: 0.3; } .contribute-card::after { - content: ''; - position: absolute; - bottom: -80px; - left: 30%; - width: 250px; - height: 250px; - border-radius: 50%; - background: var(--gold); - opacity: 0.08; + content: ""; + position: absolute; + bottom: -80px; + left: 30%; + width: 250px; + height: 250px; + border-radius: 50%; + background: var(--gold); + opacity: 0.08; } .contribute-text { - position: relative; - z-index: 1; + position: relative; + z-index: 1; } .contribute-text h2 { - font-family: 'Instrument Serif', serif; - font-size: 2.5rem; - color: var(--cream); - letter-spacing: -0.02em; - margin-bottom: 0.75rem; + font-family: "Instrument Serif", serif; + font-size: 2.5rem; + color: var(--cream); + letter-spacing: -0.02em; + margin-bottom: 0.75rem; } .contribute-text p { - font-size: 1rem; - color: rgba(246, 241, 235, 0.6); - max-width: 440px; - line-height: 1.7; + font-size: 1rem; + color: rgba(246, 241, 235, 0.6); + max-width: 440px; + /* line-height: 1.7; */ } .contribute-action { - position: relative; - z-index: 1; - flex-shrink: 0; + position: relative; + z-index: 1; + flex-shrink: 0; } .contribute-btn { - display: inline-flex; - align-items: center; - gap: 0.75rem; - padding: 1rem 2rem; - background: var(--gold); - color: var(--indigo); - font-size: 0.9rem; - font-weight: 600; - text-decoration: none; - border-radius: 12px; - letter-spacing: 0.02em; - transition: all 0.3s ease; + display: inline-flex; + align-items: center; + gap: 0.75rem; + padding: 1rem 2rem; + background: var(--gold); + color: var(--indigo); + font-size: 0.9rem; + font-weight: 600; + text-decoration: none; + border-radius: 12px; + letter-spacing: 0.02em; + transition: all 0.3s ease; } .contribute-btn:hover { - background: var(--gold-light); - transform: translateY(-2px); - box-shadow: 0 8px 24px rgba(212, 149, 42, 0.3); + background: var(--gold-light); + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(212, 149, 42, 0.3); } .contribute-btn svg { - transition: transform 0.3s ease; + transition: transform 0.3s ease; } .contribute-btn:hover svg { - transform: translateX(3px); + transform: translateX(3px); } footer { - max-width: 1300px; - margin: 0 auto; - padding: 2.5rem 2.5rem 3rem; - border-top: 1px solid rgba(27, 20, 100, 0.08); - display: flex; - align-items: center; - justify-content: space-between; + max-width: 1300px; + margin: 0 auto; + padding: 2.5rem 2.5rem 3rem; + border-top: 1px solid rgba(27, 20, 100, 0.08); + display: flex; + align-items: center; + justify-content: space-between; } .footer-brand { - font-family: 'Instrument Serif', serif; - font-size: 1.1rem; - color: var(--indigo); + font-family: "Instrument Serif", serif; + font-size: 1.1rem; + color: var(--indigo); } .footer-brand span { - color: var(--gold); + color: var(--gold); } .footer-links { - display: flex; - gap: 2rem; + display: flex; + gap: 2rem; } .footer-links a { - font-size: 0.8rem; - color: var(--text-light); - text-decoration: none; - letter-spacing: 0.04em; - transition: color 0.2s ease; + font-size: 0.8rem; + color: var(--text-light); + text-decoration: none; + letter-spacing: 0.04em; + transition: color 0.2s ease; } .footer-links a:hover { - color: var(--indigo); + color: var(--indigo); } .footer-copy { - font-size: 0.78rem; - color: var(--text-light); + font-size: 0.78rem; + color: var(--text-light); } @keyframes fadeUp { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + to { + opacity: 1; + } } @media (max-width: 900px) { - .hero-decoration { - display: none; - } - - .projects-grid { - grid-template-columns: 1fr; - } - - .contribute-card { - flex-direction: column; - padding: 3rem 2.5rem; - text-align: center; - align-items: center; - } + .hero-decoration { + display: none; + } + + .projects-grid { + grid-template-columns: 1fr; + } + + .contribute-card { + flex-direction: column; + padding: 3rem 2.5rem; + text-align: center; + align-items: center; + } - .contribute-text p { - margin: 0 auto; - } + .contribute-text p { + margin: 0 auto; + } - .stats-bar { - gap: 1.5rem; - flex-wrap: wrap; - } + .stats-bar { + gap: 1.5rem; + flex-wrap: wrap; + } } @media (max-width: 600px) { - nav { - padding: 1rem 1.25rem; - } - - .nav-links a:not(.nav-cta) { - display: none; - } - - .hero { - padding: 8rem 1.25rem 4rem; - } - - .stats-bar { - padding: 0 1.25rem; - margin-bottom: 3rem; - } - - .stat-divider { - display: none; - } - - .section-heading { - padding: 0 1.25rem 2rem; - } - - .section-heading .line { - display: none; - } - - .projects { - padding: 0 1.25rem 4rem; - } - - .project-card { - padding: 2rem; - } - - .contribute { - padding: 0 1.25rem 4rem; - } - - .contribute-card { - padding: 2.5rem 1.75rem; - } - - footer { - flex-direction: column; - gap: 1rem; - text-align: center; - padding: 2rem 1.25rem; - } -} \ No newline at end of file + .view-toggle { + display: none; + gap: 0.75rem; + } + + nav { + padding: 1rem 1.25rem; + } + + .nav-links a:not(.nav-cta) { + display: none; + } + + .hero { + padding: 8rem 1.25rem 4rem; + } + + .stats-bar { + padding: 0 1.25rem; + margin-bottom: 3rem; + } + + .stat-divider { + display: none; + } + + .section-heading { + padding: 0 1.25rem 2rem; + } + + .section-heading .line { + display: none; + } + + .projects { + padding: 0 1.25rem 4rem; + } + + .project-card { + padding: 2rem; + } + + .contribute { + padding: 0 1.25rem 4rem; + } + + .contribute-card { + padding: 2.5rem 1.75rem; + } + + footer { + flex-direction: column; + gap: 1rem; + text-align: center; + padding: 2rem 1.25rem; + } +} diff --git a/templates/base.html b/templates/base.html index 0e18463..affc469 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,21 +1,25 @@ - + - - - + + + {% block title %}PythonAsia Open Source{% endblock %} - - - - - - + + + + + + + {% block content %}{% endblock %} - - \ No newline at end of file + + diff --git a/templates/index.html b/templates/index.html index 8cc5027..5f17efd 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,164 +1,289 @@ -{% extends "base.html" %} +{% extends "base.html" %} {% block title %}PythonAsia Open Source{% endblock %} +{% block content %} + -{% block title %}PythonAsia Open Source{% endblock %} +
+ Open Source from Asia +

Software built by the Python community across Asia

+

+ A showcase of open source projects being built, maintained, and sprinted on + by Python developers throughout the region. +

-{% block content %} - + +
-
- Open Source from Asia -

Software built by the Python community across Asia

-

A showcase of open source projects being built, maintained, and sprinted on by Python developers throughout the region.

+
+
+ 4 + Projects +
+
+
+ 3 + Countries +
+
+
+ 1 + Community +
+
- -
+
+

Featured Projects

+
+
+ + +
+
-
-
- 4 - Projects -
-
-
- 3 - Countries -
-
-
- 1 - Community -
-
+
+
+
+
+ 01 + Framework +
+

Air

+

+ A modern, lightweight web framework for Python. Built for simplicity and + performance, Air provides an elegant API for building web applications + quickly. +

+
+ Python + + Web Framework + + Feldroy +
+ +
-
-

Featured Projects

-
-
+
+
+ 02 + Generator +
+

Render Engine

+

+ A flexible Python-based static site generator. Create beautiful, fast + websites with a powerful templating system and plugin architecture. +

+
+ Python + + Static Sites +
+ +
-
-
-
-
- 01 - Framework -
-

Air

-

A modern, lightweight web framework for Python. Built for simplicity and performance, Air provides an elegant API for building web applications quickly.

-
- Python - - Web Framework - - Feldroy -
- -
+
+
+ 03 + Tools +
+

Organizer Toolkit

+

+ A collection of tools and resources for organizing Python community + events. Built by the Python Software Foundation to help event organizers + succeed. +

+
+ Python + + Community + + PSF +
+ +
-
-
- 02 - Generator -
-

Render Engine

-

A flexible Python-based static site generator. Create beautiful, fast websites with a powerful templating system and plugin architecture.

-
- Python - - Static Sites -
- -
+
+
+ 04 + Community +
+

Cardiff

+

+ An open source contribution from the Filipino Python community at + Z-California PH. Explore and contribute to this growing project. +

+
+ Python + + Philippines +
+ +
+
+
+ +
+
+
+

Share your project

+

+ Building something in the open? We want to feature Python open source + projects from developers across Asia. Submit yours to be showcased. +

+
+ +
+
-
-
- 03 - Tools -
-

Organizer Toolkit

-

A collection of tools and resources for organizing Python community events. Built by the Python Software Foundation to help event organizers succeed.

-
- Python - - Community - - PSF -
- -
+ -
-
- 04 - Community -
-

Cardiff

-

An open source contribution from the Filipino Python community at Z-California PH. Explore and contribute to this growing project.

-
- Python - - Philippines -
- -
-
-
+ +{% endblock %} From 1b35c860cad315a18fa9030d7388a4f07f44a8ef Mon Sep 17 00:00:00 2001 From: Eithan Malonzo Date: Mon, 23 Mar 2026 16:16:43 +0800 Subject: [PATCH 2/2] Refactor project data to JSON --- main.py | 10 ++- projects.json | 47 +++++++++++++ templates/index.html | 156 ++++++------------------------------------- 3 files changed, 78 insertions(+), 135 deletions(-) create mode 100644 projects.json diff --git a/main.py b/main.py index c8d3620..c9f4882 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import json import air app = air.Air() @@ -5,4 +6,11 @@ @app.page def index(request: air.Request): - return app.jinja(request, "index.html", title="PythonAsia Open Source") + with open("projects.json", "r") as f: + projects_data = json.load(f) + return app.jinja( + request, + "index.html", + title="PythonAsia Open Source", + projects=projects_data, + ) diff --git a/projects.json b/projects.json new file mode 100644 index 0000000..9528f04 --- /dev/null +++ b/projects.json @@ -0,0 +1,47 @@ +[ + { + "id": "01", + "name": "Air", + "tag": "Framework", + "tagClass": "framework", + "description": "A modern, lightweight web framework for Python. Built for simplicity and performance, Air provides an elegant API for building web applications quickly.", + "meta": ["Python", "Web Framework", "Feldroy"], + "links": { + "website": "https://airwebframework.org/", + "github": "https://github.com/feldroy/air" + } + }, + { + "id": "02", + "name": "Render Engine", + "tag": "Generator", + "tagClass": "generator", + "description": "A flexible Python-based static site generator. Create beautiful, fast websites with a powerful templating system and plugin architecture.", + "meta": ["Python", "Static Sites"], + "links": { + "github": "https://github.com/render-engine/render-engine" + } + }, + { + "id": "03", + "name": "Organizer Toolkit", + "tag": "Tools", + "tagClass": "tools", + "description": "A collection of tools and resources for organizing Python community events. Built by the Python Software Foundation to help event organizers succeed.", + "meta": ["Python", "Community", "PSF"], + "links": { + "github": "https://github.com/psf/organizer-toolkit" + } + }, + { + "id": "04", + "name": "Cardiff", + "tag": "Community", + "tagClass": "community", + "description": "An open source contribution from the Filipino Python community at Z-California PH. Explore and contribute to this growing project.", + "meta": ["Python", "Philippines"], + "links": { + "github": "https://github.com/zcalifornia-ph/cardiff" + } + } +] diff --git a/templates/index.html b/templates/index.html index 5f17efd..e1afcf1 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,4 +1,7 @@ -{% extends "base.html" %} {% block title %}PythonAsia Open Source{% endblock %} +{% extends "base.html" %} + +{% block title %}PythonAsia Open Source{% endblock %} + {% block content %}