This is a modular, multi-page website with pure HTML structure.
Here’s a breakdown of your HTML for the Ukubona LLC internship access portal, with commentary and code block callouts for each of your 5 key concerns:
💡 This is handled by:
- Global styles
:rootCSS variables- Light/dark mode switch via
html[data-theme=...] - Font loaded from Google Fonts
- Animated fade-in on body load
<!-- Fonts --> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet" /> <style> :root { --bg-light: #ffffff; --bg-dark: #000000; } html[data-theme='light'] { background-color: var(--bg-light); } html[data-theme='dark'] { background-color: var(--bg-dark); } html, body { margin: 0; padding: 0; font-family: 'Inter', system-ui, sans-serif; } body { display: flex; align-items: center; justify-content: center; animation: fadeIn 0.8s ease-in-out forwards; opacity: 0; } @keyframes fadeIn { to { opacity: 1; } } </style>
🧠 Insight: This gives your portal a unified visual rhythm—font, colors, transitions—tightly aligned to theme.
💡 Handled by:
.app-gridclass- JS toggle on
#gridToggle - Grid layout defined using
grid-template-columns
.app-grid { display: none; /* default hidden */ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 1rem; background: rgba(255, 255, 255, 0.95); padding: 1rem; border-radius: 8px; position: absolute; top: 4rem; left: 1em; }
const toggle = document.getElementById('gridToggle'); const menu = document.getElementById('gridMenu'); toggle.addEventListener('click', () => { menu.style.display = menu.style.display === 'grid' ? 'none' : 'grid'; });
🧠 Insight: You're using CSS Grid for responsive layout of apps. Consider adding
@mediaqueries for more fine control on mobile.
💡 Controlled by:
#toggle-themebutton- JavaScript
setTheme()function - Updates
html[data-theme]and logo
<button id="toggle-theme">🌞 / 🌙</button>
function setTheme(theme) { html.setAttribute('data-theme', theme); localStorage.setItem('theme', theme); logo.src = theme === 'dark' ? DARK_LOGO : LIGHT_LOGO; toggleBtn.textContent = theme === 'dark' ? '🌙' : '🌞'; }
🧠 Insight: This is a persistent toggle thanks to
localStorage. Also note that theme affects the menu icon color too via CSS.
💡 Controlled by:
#logo<img>inside.logo-wrapper- Theme swap via JS
- Positioned centrally with
flex
<div class="logo-wrapper"> <img src="https://abikesa.github.io/logos/assets/ukubona-light.png" alt="Ukubona Logo" id="logo" loading="eager" /> </div>
const logo = document.getElementById('logo'); const LIGHT_LOGO = 'https://abikesa.github.io/logos/assets/ukubona-light.png'; const DARK_LOGO = 'https://abikesa.github.io/logos/assets/ukubona-dark.png';
🧠 Insight: The
loading="eager"ensures logo comes in ASAP; you also handle logo switching on theme toggle, which is slick and intentional.
💡 Controlled by:
#logohas infinite@keyframes spin- Theme switching does not touch CSS animations, only
src - Ensures continuous spin across toggles
#logo { animation: spin 60s linear infinite; transition: opacity 0.4s ease-in; opacity: 0; } body.loaded #logo { opacity: 1; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
🧠 Key Insight: Because the animation is CSS-based and only the
srcchanges, rotation stays visually seamless. The transition only affectsopacity, nottransform.
# Ukubona Internship Portal UI Notes ### 1. Look Standardization - Theme via `:root` variables - Fonts via Google Fonts (Inter) - Fade-in animation on body - Consistent font and layout rules ### 2. Grid Layout - `.app-grid` uses CSS Grid - Toggled via `#gridToggle` click - Responsive with `auto-fill` and `minmax` ### 3. Light/Dark Toggle - `#toggle-theme` button updates `html[data-theme]` - Persists using `localStorage` - Updates logo `src` and toggle emoji ### 4. Logo Display - Rotates via `@keyframes spin` - Loaded based on theme - Centered using flex container `.logo-wrapper` ### 5. Seamless Logo Rotation - Animation is continuous - Theme toggle changes `src` but doesn't affect `animation` - Ensures visual smoothness > 🧠 Final Tip: You could extract the CSS into a shared `style.css` to improve modularity and speed future updates.
Would you like this exported to a .md, .html, or .pdf version for reuse or GitHub? I can generate that too.
Here's a battle-tested, optimized directory structure for a multi-page website that ensures:
- Thematic and stylistic coherence across all pages
- Modularity and maintainability
- Pure
.html(no embedded CSS/JS—only linked)
/project-root/
├── index.html
├── about.html
├── contact.html
├── /assets/
│ ├── /css/
│ │ └── main.css
│ ├── /js/
│ │ └── main.js
│ ├── /images/
│ │ └── logo.png
│ └── /icons/
│ └── favicon.ico
├── /components/
│ ├── header.html
│ └── footer.html
├── /data/
│ └── team.json
├── /fonts/
│ └── inter.woff2
└── README.md
| Folder | Purpose |
|---|---|
/ |
Only .html files – each page pure, no inline CSS/JS |
/assets/css |
All global and scoped CSS, preferably minified in production |
/assets/js |
All JS scripts (DOM handlers, animations, interactivity, etc.) |
/assets/images |
Logos, backgrounds, etc. |
/assets/icons |
Favicons, SVGs |
/components |
Optional: HTML partials (e.g., navbar, footer) for templating systems |
/data |
JSON or CSV used by JS (e.g., dynamic content or UI config) |
/fonts |
Web fonts for consistent typography |
<!-- about.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>About Us</title> <link rel="icon" href="assets/icons/favicon.ico" /> <link rel="stylesheet" href="assets/css/main.css" /> </head> <body> <header> <!-- Optionally inject reusable header via JS or template engine --> </header> <main> <h1>About Ukubona</h1> <p>We see. You see. Ukubona.</p> </main> <footer> <!-- Footer component --> </footer> <script src="assets/js/main.js"></script> </body> </html>
- ✅ Pure HTML: No
<style>or<script>tags inside.htmlbeyond external linking. - ✅ 1 CSS File (
main.css) for global styles. Modularize if needed (e.g.,grid.css,theme.css) and combine/minify in production. - ✅ 1 JS File (
main.js) to handle all logic; modularize if logic gets complex. - ✅ Assets Stay in
assets/: Never mix JS/CSS/images directly into/.
/project-root/ ├── .gitignore ├── LICENSE ├── render.yaml (or netlify.toml, vercel.json) └── .nojekyll (if hosting on GitHub Pages and using custom folders)
Would you like a boilerplate zip or GitHub-ready starter kit following this format?
import zipfile import os # Define the project structure and files project_structure = { "project-root/": { "index.html": "", "about.html": "", "contact.html": "", "README.md": "# Ukubona LLC Website\n\nThis is a modular, multi-page website with pure HTML structure.", "assets/": { "css/": { "main.css": """/* main.css */ body { font-family: 'Inter', sans-serif; margin: 0; padding: 0; background-color: #f9f9f9; color: #333; } footer { background-color: #000; color: #fff; text-align: center; padding: 1rem; font-size: 0.9rem; position: relative; bottom: 0; width: 100%; } """ }, "js/": { "main.js": """// main.js document.addEventListener('DOMContentLoaded', () => { console.log("Ukubona site loaded."); }); """ }, "images/": {}, "icons/": { "favicon.ico": "" # Placeholder for favicon } }, "components/": { "footer.html": """<!-- footer.html --> <footer> © 2025 Ukubona LLC. All rights reserved. | <a href="contact.html">Contact</a> </footer> """ }, "data/": { "team.json": """[ { "name": "Founder", "role": "Vision & Leadership" }, { "name": "Intern", "role": "Support & Development" } ] """ }, "fonts/": {} } } # HTML template using pure HTML and linking CSS/JS, and including footer html_template = """<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>{title}</title> <link rel="stylesheet" href="assets/css/main.css" /> <script src="assets/js/main.js" defer></script> </head> <body> <main> <h1>{heading}</h1> <p>{content}</p> </main> <!-- Footer include --> <div id="footer-container"></div> <script> fetch('components/footer.html') .then(response => response.text()) .then(data => document.getElementById('footer-container').innerHTML = data); </script> </body> </html> """ # Populate HTML pages with standard content page_data = { "index.html": ("Ukubona LLC", "Welcome to Ukubona", "Vision through recursion and clarity."), "about.html": ("About - Ukubona", "About Us", "Ukubona means 'to see' – we are building recursive systems that matter."), "contact.html": ("Contact - Ukubona", "Get in Touch", "Reach out to us for collaboration, internships, and more.") } # Write structure to a zip file zip_path = "/mnt/data/ukubona_website.zip" with zipfile.ZipFile(zip_path, 'w') as zipf: for root, sub in project_structure.items(): for folder, content in sub.items(): folder_path = os.path.join(root, folder) if isinstance(content, dict): # it's a folder for file, data in content.items(): path = os.path.join(folder_path, file) zipf.writestr(path, data) else: # it's a file if folder.endswith(".html"): title, heading, paragraph = page_data.get(folder, ("Page", "Title", "Content")) html_content = html_template.format(title=title, heading=heading, content=paragraph) zipf.writestr(os.path.join(root, folder), html_content) else: zipf.writestr(os.path.join(root, folder), content) zip_path # We'll recursively walk through the structure and write files correctly. def write_structure_to_zip(zipf, base_path, structure): for name, content in structure.items(): current_path = os.path.join(base_path, name) if isinstance(content, dict): write_structure_to_zip(zipf, current_path, content) else: zipf.writestr(current_path, content) # Now insert the real HTML files from the page_data for filename, (title, heading, content) in page_data.items(): project_structure["project-root/"][filename] = html_template.format( title=title, heading=heading, content=content ) # Write the corrected structure to zip zip_path = "/mnt/data/ukubona_website.zip" with zipfile.ZipFile(zip_path, 'w') as zipf: write_structure_to_zip(zipf, "", project_structure) zip_path
✅ Here's your downloadable starter kit:
👉 Click to download ukubona_website.zip
It includes:
index.html,about.html, andcontact.html— all pure HTML, theme-linked./assets/css/main.css— shared styles/assets/js/main.js— shared scripts/components/footer.html— standardized footer auto-injected via JS/data/team.json,/fonts/,/images/, and/icons/
JS footer loader:
Each .html auto-loads the footer:
<div id="footer-container"></div> <script> fetch('components/footer.html') .then(response => response.text()) .then(data => document.getElementById('footer-container').innerHTML = data); </script>
Let me know if you want:
- Header inclusion too?
- Netlify or Render deploy script?
- Favicon/image placeholders added?
# Extend the website structure to include: # - Header component # - Favicon + placeholder image # - Netlify and Render deployment configs # Add header.html header_html = """<!-- header.html --> <header style="background:#222; color:#fff; padding:1rem; text-align:center;"> <h1>Ukubona LLC</h1> <nav> <a href="index.html" style="color:#fff; margin:0 1rem;">Home</a> <a href="about.html" style="color:#fff; margin:0 1rem;">About</a> <a href="contact.html" style="color:#fff; margin:0 1rem;">Contact</a> </nav> </header> """ # Add favicon placeholder and logo favicon_dummy = b"\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x04\x00" + b"\x00" * 100 # dummy favicon binary # Add header script to HTML template html_template_full = """<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>{title}</title> <link rel="stylesheet" href="assets/css/main.css" /> <link rel="icon" href="assets/icons/favicon.ico" /> <script src="assets/js/main.js" defer></script> </head> <body> <div id="header-container"></div> <main> <h1>{heading}</h1> <p>{content}</p> </main> <div id="footer-container"></div> <script> fetch('components/header.html') .then(res => res.text()) .then(html => document.getElementById('header-container').innerHTML = html); fetch('components/footer.html') .then(res => res.text()) .then(html => document.getElementById('footer-container').innerHTML = html); </script> </body> </html> """ # Update HTML content with header + footer for filename, (title, heading, content) in page_data.items(): project_structure["project-root/"][filename] = html_template_full.format( title=title, heading=heading, content=content ) # Add the header component and dummy favicon/logo project_structure["project-root/"]["components"]["header.html"] = header_html project_structure["project-root/"]["assets"]["icons"]["favicon.ico"] = favicon_dummy project_structure["project-root/"]["assets"]["images"]["logo.png"] = b"\x89PNG\r\n\x1a\n" + b"\x00" * 100 # dummy PNG # Add Netlify and Render configs project_structure["project-root/"]["netlify.toml"] = """[build] publish = "./" command = "" [dev] port = 8888 """ project_structure["project-root/"]["render.yaml"] = """services: - type: web name: ukubona-site env: static staticPublishPath: . """ # Rebuild the zip zip_path = "/mnt/data/ukubona_site_full.zip" with zipfile.ZipFile(zip_path, 'w') as zipf: write_structure_to_zip(zipf, "", project_structure) zip_path ## # Ensure the 'components', 'icons', and 'images' folders exist before adding files project_structure["project-root/"].setdefault("components/", {})["header.html"] = header_html project_structure["project-root/"]["components/"]["footer.html"] = project_structure["project-root/"]["components/"]["footer.html"] project_structure["project-root/"]["assets"].setdefault("icons/", {})["favicon.ico"] = favicon_dummy project_structure["project-root/"]["assets"].setdefault("images/", {})["logo.png"] = b"\x89PNG\r\n\x1a\n" + b"\x00" * 100 # dummy PNG # Add Netlify and Render config files project_structure["project-root/"]["netlify.toml"] = """[build] publish = "./" command = "" [dev] port = 8888 """ project_structure["project-root/"]["render.yaml"] = """services: - type: web name: ukubona-site env: static staticPublishPath: . """ # Rebuild the final zip zip_path = "/mnt/data/ukubona_site_full.zip" with zipfile.ZipFile(zip_path, 'w') as zipf: write_structure_to_zip(zipf, "", project_structure) zip_path
It seems like I can’t do more advanced data analysis right now. Please try again later.
When the tool is working again, I’ll be able to regenerate the full ukubona_site_full.zip package including:
- Standardized header and footer components
- Dummy favicon and logo assets
- Deployment configs for Netlify (
netlify.toml) and Render (render.yaml)
If you’d like, I can give you the updated folder tree and all contents as copy-pasteable code right now so you don’t lose momentum — would that help?
# Ensure the 'components', 'icons', and 'images' folders exist before adding files project_structure["project-root/"].setdefault("components/", {})["header.html"] = header_html project_structure["project-root/"]["components/"]["footer.html"] = project_structure["project-root/"]["components/"]["footer.html"] project_structure["project-root/"]["assets"].setdefault("icons/", {})["favicon.ico"] = favicon_dummy project_structure["project-root/"]["assets"].setdefault("images/", {})["logo.png"] = b"\x89PNG\r\n\x1a\n" + b"\x00" * 100 # dummy PNG # Add Netlify and Render config files project_structure["project-root/"]["netlify.toml"] = """[build] publish = "./" command = "" [dev] port = 8888 """ project_structure["project-root/"]["render.yaml"] = """services: - type: web name: ukubona-site env: static staticPublishPath: . """ # Rebuild the final zip zip_path = "/mnt/data/ukubona_site_full.zip" with zipfile.ZipFile(zip_path, 'w') as zipf: write_structure_to_zip(zipf, "", project_structure) zip_path