SageFlow
Built by Sazzad Hossain — because good design deserves good defaults. Grab the tokens, steal the components, copy-paste the markup. No build tools. No drama. Just drop in the CSS & JS and ship something beautiful.
Getting Started
Download the two standalone files and add them to your project. No build tools, no dependencies — just HTML, CSS, and vanilla JS.
1 — Add to your <head>
<!-- Google Fonts (required) --> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link href="https://fonts.googleapis.com/css2?family=Funnel+Display:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;600&display=swap" rel="stylesheet" /> <!-- SageFlow CSS --> <link rel="stylesheet" href="sageflow/sageflow.css" />
2 — Add before </body>
<!-- Lucide Icons (optional, for icon support) --> <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script> <!-- SageFlow JS --> <script src="sageflow/sageflow.js"></script> <!-- Init Lucide icons --> <script>lucide.createIcons();</script>
3 — Dark mode toggle
<!-- Add data-sf-dark-toggle to any button --> <button data-sf-dark-toggle aria-pressed="false">Toggle Dark Mode</button> <!-- Or call programmatically --> <script>SageFlow.toggleDark();</script>
Design Tokens
All values live as CSS custom properties on :root. Dark mode overrides are applied via [data-theme="dark"] on <html>.
Primary & Accent
Backgrounds (Light Mode)
Text
Dark Mode Tokens
/* In assets/css/main.css */
:root {
--color-primary: #7A9E8A;
--color-accent: #EDD9A3;
--color-bg-primary: #FAF7F6;
--color-surface: #ffffff;
--color-border: #e5e5e5;
--color-text-primary: #2c2c2c;
--color-text-secondary: #6b6b6b;
--color-dark: #000000;
--font-display: "Funnel Display", sans-serif;
--font-body: "Inter", -apple-system, sans-serif;
--font-mono: "IBM Plex Mono", monospace;
}
[data-theme="dark"] {
--color-primary: #8FB4A0;
--color-bg-primary: #0e1a14;
--color-surface: #1a3a2d;
--color-border: #274035;
--color-text-primary: #e8e8e8;
--color-text-secondary: #9db8ab;
}
Heading Scale
Six semantic heading classes mapping to h1–h6. Apply as classes on any element.
<h1 class="sf-h1">Display Heading</h1>
<h2 class="sf-h2">Section Heading</h2>
<h3 class="sf-h3">Subsection Heading</h3>
<h4 class="sf-h4">Card Title</h4>
<h5 class="sf-h5">Small Title</h5>
<h6 class="sf-h6">Label Heading</h6>
<!-- Blockquote -->
<blockquote class="sf-blockquote">
Design is not just what it looks like — it's how it works.
<cite>Steve Jobs</cite>
</blockquote>
<!-- Inline code -->
Use the <code class="sf-code">sf-btn</code> class to style buttons.
Typography
Three typefaces with clear roles. Funnel Display for everything structural, Inter for reading, IBM Plex Mono for labels and metadata.
Selected Work
BeeBack AR Platform
Portfolio
15+ years designing digital products — from research and wireframes to shipped code.
Regular body copy for paragraphs, descriptions, and longer reading content.
Spacing & Layout
Three container widths, consistent section padding, and a predictable grid system.
/* Containers */
.container { max-width: 1430px; margin: 0 auto; padding: 0 40px; }
.container-xl { max-width: 1750px; margin: 0 auto; padding: 0 40px; }
.container-md { max-width: 968px; margin: 0 auto; padding: 0 40px; }
/* Section padding (standard) */
section { padding: 100px 0; }
/* Pill section (sage background) */
.skills-section,
.testimonials-section {
width: 94%;
margin: 0 auto 80px;
border-radius: 28px;
padding: 100px 0;
}
/* Common grid templates */
.portfolio-grid { grid-template-columns: repeat(2, 1fr); gap: 24px; }
.testimonials-grid { grid-template-columns: repeat(3, 1fr); gap: 20px; }
.toolbox-grid { grid-template-columns: repeat(4, 1fr); gap: 14px; }
.skill-categories { grid-template-columns: repeat(4, 1fr); gap: 24px; }
Buttons
Three button variants. All use pill shape, flex layout with icon slot, and consistent hover lift.
↑ Hover to see lift + border transitions
<!-- Primary: black fill → outlined on hover -->
<a href="#" class="btn btn-primary">
<span>Get in Touch</span>
<i data-lucide="arrow-right" class="btn-icon"></i>
</a>
<!-- Secondary: outlined → black fill on hover -->
<a href="#" class="btn btn-secondary">
<span>View All Projects</span>
<i data-lucide="arrow-right" class="btn-icon"></i>
</a>
Send Button (Contact form)
<!-- Send: mono font, pill, dark → sage on hover, gap animates -->
<button class="btn-send">
<span>Send it over</span>
<i data-lucide="arrow-right"></i>
</button>
Button Sizes & Variants
All buttons use the base .sf-btn class. Combine with a variant class and an optional size modifier.
Sizes
<button class="sf-btn sf-btn-primary sf-btn-sm">Small</button> <button class="sf-btn sf-btn-primary">Default</button> <button class="sf-btn sf-btn-primary sf-btn-lg">Large</button> <button class="sf-btn sf-btn-primary sf-btn-xl">XL</button>
Variants
<button class="sf-btn sf-btn-primary">Primary</button>
<button class="sf-btn sf-btn-secondary">Secondary</button>
<button class="sf-btn sf-btn-sage">Sage</button>
<button class="sf-btn sf-btn-ghost">Ghost</button>
<button class="sf-btn sf-btn-danger">Danger</button>
<button class="sf-btn sf-btn-success">Success</button>
<!-- Loading state -->
<button class="sf-btn sf-btn-primary is-loading">Saving...</button>
<!-- Icon only -->
<button class="sf-btn sf-btn-secondary sf-btn-icon">
<i data-lucide="arrow-right"></i>
</button>
Badges & Labels
Used to communicate skill level, availability, section context, and metadata.
Section Label
Portfolio
<p class="section-label">Portfolio</p> /* IBM Plex Mono · 13px · uppercase · letter-spacing 2–3px · color: primary */
Tool Level Badges
<span class="tool-level tool-level--expert">Expert</span> <span class="tool-level tool-level--proficient">Proficient</span> <span class="tool-level tool-level--familiar">Familiar</span>
Availability Dot
<div class="hero-availability">
<span class="avail-dot"></span>
Available for new roles
</div>
/* Green dot · 8px · pulsing box-shadow animation */
Footer Availability Status
Tags & Chips
Compact labels for categories, status, and metadata. Six color variants plus a removable chip pattern.
<span class="sf-tag sf-tag-default">Default</span>
<span class="sf-tag sf-tag-sage">Sage</span>
<span class="sf-tag sf-tag-gold">Gold</span>
<span class="sf-tag sf-tag-green">Green</span>
<span class="sf-tag sf-tag-red">Red</span>
<span class="sf-tag sf-tag-blue">Blue</span>
<!-- Removable chip -->
<span class="sf-tag sf-tag-sage">
Design Systems
<button class="sf-tag-remove"><i data-lucide="x"></i></button>
</span>
<!-- Tag list -->
<div class="sf-tag-list">
<span class="sf-tag sf-tag-sage">UX</span>
<span class="sf-tag sf-tag-default">Research</span>
</div>
Avatar
Five sizes, image or initials fallback, optional ring, and an overlapping group layout.
XS · SM · MD · LG · XL — with ring on hover
<!-- With image -->
<div class="sf-avatar sf-avatar-lg">
<img src="photo.jpg" alt="Sazzad Hossain" />
</div>
<!-- Initials fallback -->
<div class="sf-avatar sf-avatar-lg">SH</div>
<!-- Ring -->
<div class="sf-avatar sf-avatar--ring">SH</div>
<!-- Group (overlapping) -->
<div class="sf-avatar-group">
<div class="sf-avatar sf-avatar-sm">AB</div>
<div class="sf-avatar sf-avatar-sm">CD</div>
<div class="sf-avatar sf-avatar-sm">EF</div>
</div>
<!-- Sizes: sf-avatar-xs / sm / (default) / lg / xl -->
Table
Fully styled data table with header, hover rows, optional stripes, and a compact mode. Wraps in .sf-table-wrap for overflow handling.
<div class="sf-table-wrap">
<table class="sf-table">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th class="text-right">Projects</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sazzad Hossain</td>
<td>Product Designer</td>
<td class="text-right">24</td>
</tr>
</tbody>
</table>
</div>
<!-- Striped -->
<table class="sf-table sf-table--striped">...</table>
<!-- Compact -->
<table class="sf-table sf-table--compact">...</table>
Cards
All cards share: surface background, 1px border, 20–24px radius, and a lift + left sage accent on hover.
Portfolio Item Card
↑ Hover to see lift + inset left sage accent
<a href="case-studies/project.html" class="portfolio-item fade-in">
<div class="portfolio-image">
<img src="assets/images/case-study/project/cover.png" alt="Project Name">
</div>
<div class="portfolio-info">
<div class="portfolio-meta">
<span>Category</span>
<span>Year</span>
</div>
<div class="portfolio-item-title-row">
<h3 class="portfolio-title">Project Title</h3>
<i data-lucide="arrow-right" class="portfolio-arrow"></i>
</div>
</div>
</a>
Toolbox Item Card
<div class="toolbox-grid">
<div class="tool-item">
<div class="tool-icon">
<!-- 40×40 SVG or Lucide icon -->
</div>
<div class="tool-info">
<span class="tool-name">Figma</span>
<span class="tool-level tool-level--expert">Expert</span>
</div>
</div>
</div>
/* Level modifiers: tool-level--expert | tool-level--proficient | tool-level--familiar */
Testimonial Card
"Sazzad is one of the most versatile UX designers I have ever worked with. His knowledge spans front-end engineering, growth, marketing, and more."
Read full story →"His passionate commitment to understanding the needs of the end-user is simply UNRIVALED, and it shows in the consistency of organic, scalable UIUX practices."
Read full story →"His designs are comprehensive and well thought out. Sazzad effectively bridges the gap between user requirements and code."
Read full story →↑ Sage background (#E4F0EA) is the section background, not the card
<div class="t-card fade-in">
<p class="t-quote">"Visible excerpt shown on card..."</p>
<span class="t-read-hint">Read full story →</span>
<p class="t-full-quote" hidden>"Full text opened in modal..."</p>
<div class="t-author">
<img src="..." alt="Name" class="t-avatar">
<div class="t-author-info">
<strong>Full Name</strong>
<span>Title, Company</span>
</div>
</div>
</div>
Contact / Info Card
Location
New York City, NY
Open to fully remote
<div class="contact-card">
<h3>Card Heading</h3>
<div class="contact-item">
<strong>Label</strong>
<p>Value or content</p>
</div>
</div>
Skill Category Card
Product Design
- UX/UI Design
- User Research
- Prototyping
- Design Systems
Development
- HTML & CSS
- JavaScript
- Responsive Design
- Accessibility
<div class="skill-category fade-in">
<div class="skill-category-icon">
<i data-lucide="layout-template"></i>
</div>
<h3>Category Name</h3>
<ul>
<li>Skill One</li>
<li>Skill Two</li>
</ul>
</div>
Accordion
Collapsible panels driven by the is-open class. Toggle with SageFlow JS or your own handler.
<div class="sf-accordion">
<div class="sf-accordion-item is-open">
<button class="sf-accordion-trigger">
What is SageFlow?
<i data-lucide="chevron-down" class="sf-accordion-icon"></i>
</button>
<div class="sf-accordion-body">
<div class="sf-accordion-content">
SageFlow is a sage-green design system...
</div>
</div>
</div>
<div class="sf-accordion-item">...</div>
</div>
Tabs
Underline tabs and pill-style tabs. Panel visibility toggled with is-active class via SageFlow JS.
Underline tabs
Pill tabs
<!-- Underline tabs -->
<div class="sf-tabs" data-sf-tabs>
<div class="sf-tabs-list">
<button class="sf-tab-btn is-active" data-sf-tab="overview">Overview</button>
<button class="sf-tab-btn" data-sf-tab="process">Process</button>
</div>
<div class="sf-tab-panel is-active" data-sf-panel="overview">...</div>
<div class="sf-tab-panel" data-sf-panel="process">...</div>
</div>
<!-- Pill tabs -->
<div class="sf-tabs sf-tabs--pills" data-sf-tabs>
<div class="sf-tabs-list">
<button class="sf-tab-btn is-active" data-sf-tab="all">All</button>
<button class="sf-tab-btn" data-sf-tab="ux">UX</button>
</div>
...
</div>
Form Elements
Standard input, select, textarea, checkbox, radio, toggle, and range. All support dark mode and validation states.
Input
We'll never share your email.
This field is required.
Select, Textarea, Toggle, Range
<!-- Text input -->
<div class="sf-form-group">
<label class="sf-label">Full Name</label>
<input type="text" class="sf-input" placeholder="Sazzad Hossain" />
<p class="sf-field-helper">Helper text here.</p>
</div>
<!-- Error state -->
<input type="text" class="sf-input sf-input--error" />
<p class="sf-field-error">This field is required.</p>
<!-- Select -->
<select class="sf-select"><option>Option A</option></select>
<!-- Textarea -->
<textarea class="sf-textarea-field" rows="4"></textarea>
<!-- Checkbox -->
<label class="sf-check-label">
<input type="checkbox" class="sf-check" /> Agree to terms
</label>
<!-- Radio -->
<label class="sf-radio-label">
<input type="radio" class="sf-radio" name="group" /> Option A
</label>
<!-- Toggle -->
<label class="sf-toggle-label">
<input type="checkbox" class="sf-toggle" /> Enable notifications
</label>
<!-- Range -->
<input type="range" class="sf-range" min="0" max="100" value="65" />
Form Elements
The contact form uses a conversational fill-in-the-blank pattern. Inline inputs are underline-only; the message field is a standard bordered textarea.
Inline Input (sentence-style)
<div class="form-sentence">
<div class="form-line">
<span class="form-text">Hi, I'm</span>
<div class="inline-field-wrap">
<input type="text" name="name" placeholder="your name"
class="inline-input" required>
</div>
</div>
</div>
/* .inline-input: no border except bottom underline in --color-primary
focus: --color-secondary underline */
Textarea
<textarea class="form-message" rows="4" name="message" required
placeholder="Here's a bit more context..."></textarea>
/* bg: --color-bg-primary · border: --color-border · border-radius: 12px
focus: border → primary · box-shadow: 0 0 0 3px rgba(primary, 0.12) */
Social Link Row
<div class="social-links-list">
<a href="#" class="social-link">
<i data-lucide="linkedin"></i>
<span>LinkedIn</span>
<i class="link-arrow" data-lucide="arrow-right"></i>
</a>
</div>
/* hover: bg sage tint · border → primary · padding-left animates
.link-arrow slides in from left on hover */
Breadcrumb
Monospace uppercase navigation trail for hierarchical pages.
<nav>
<ol class="sf-breadcrumb">
<li><a href="/">Home</a></li>
<li><span class="sf-breadcrumb-sep">/</span></li>
<li><a href="/case-studies">Case Studies</a></li>
<li><span class="sf-breadcrumb-sep">/</span></li>
<li>Aktiv Design Language</li>
</ol>
</nav>
Pagination
Page navigation with active, hover, and disabled states. Works as links or buttons.
<nav>
<ul class="sf-pagination">
<li class="is-disabled"><a href="#"><i data-lucide="chevron-left"></i></a></li>
<li><a href="#">1</a></li>
<li class="is-active"><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">…</a></li>
<li><a href="#">8</a></li>
<li><a href="#"><i data-lucide="chevron-right"></i></a></li>
</ul>
</nav>
Alerts & Callouts
Five semantic variants: info, success, warning, error, and sage. Add an icon and optional dismiss button.
<div class="sf-alert sf-alert-info">
<span class="sf-alert-icon"><i data-lucide="info"></i></span>
<div>
<span class="sf-alert-title">Info</span>
<span class="sf-alert-body">Your session expires in 30 minutes.</span>
</div>
<button class="sf-alert-close"><i data-lucide="x"></i></button>
</div>
<!-- Variants: sf-alert-info / success / warning / error / sage -->
Progress Bar
Linear progress bars in three sizes and five color variants. Optional striped animation.
<!-- With label -->
<div class="sf-progress-header">
<span class="sf-progress-label">Proficiency</span>
<span class="sf-progress-value">85%</span>
</div>
<div class="sf-progress">
<div class="sf-progress-bar" style="width: 85%"></div>
</div>
<!-- Color variants -->
<div class="sf-progress-bar sf-progress-bar--success" style="width:60%"></div>
<div class="sf-progress-bar sf-progress-bar--warning" style="width:40%"></div>
<div class="sf-progress-bar sf-progress-bar--error" style="width:25%"></div>
<!-- Sizes: sf-progress-sm / (default) / sf-progress-lg -->
<!-- Striped animated -->
<div class="sf-progress">
<div class="sf-progress-bar sf-progress-bar--striped" style="width:65%"></div>
</div>
Spinner & Skeleton
Loading spinner in three sizes and a shimmer skeleton for content placeholders.
Spinners
Skeleton Loaders
<!-- Spinner --> <div class="sf-spinner"></div> <div class="sf-spinner sf-spinner-sm"></div> <div class="sf-spinner sf-spinner-lg"></div> <!-- Skeleton lines --> <div class="sf-skeleton sf-skeleton-line"></div> <div class="sf-skeleton sf-skeleton-line sf-skeleton-line--short"></div> <!-- Skeleton circle --> <div class="sf-skeleton sf-skeleton-circle"></div> <!-- Skeleton image block --> <div class="sf-skeleton sf-skeleton-block"></div>
Modal
Dark forest modal used for full testimonials. Always dark regardless of page theme. Sage-green inset left accent via box-shadow.
<div class="t-modal" id="testimonialModal" role="dialog" aria-modal="true" aria-hidden="true">
<div class="t-modal-backdrop"></div>
<div class="t-modal-box">
<button class="t-modal-close" aria-label="Close">
<i data-lucide="x"></i>
</button>
<div class="t-modal-scroll">
<div class="t-modal-quote-mark">"</div>
<p class="t-modal-text"></p>
</div>
<div class="t-modal-author">
<img class="t-modal-avatar" src="" alt="">
<div class="t-modal-author-info">
<strong class="t-modal-name"></strong>
<span class="t-modal-role"></span>
</div>
</div>
</div>
</div>
/* Open: modal.classList.add('is-open') + aria-hidden="false"
Close: ESC key · backdrop click · close button */
Stats Strip
Full-width strip with animated counter numbers. Counters increment from 0 to target on scroll using Intersection Observer.
<section class="stats-section">
<div class="container">
<div class="stats-strip">
<div class="stat-item fade-in">
<div class="stat-number" data-target="15" data-suffix="+">15+</div>
<div class="stat-label">Years Experience</div>
</div>
<div class="stat-divider" aria-hidden="true"></div>
<!-- repeat for each stat -->
</div>
</div>
</section>
/* Counter animation driven by data-target + data-suffix in main.js */
Section Structure
Two section treatments: standard (plain background) and pill (sage green, 94% width, rounded).
Standard Section
<section id="work" class="portfolio-section">
<div class="container">
<div class="portfolio-header">
<div class="fade-in">
<p class="section-label">Portfolio</p>
<h2 class="heading-1">Selected<br />Work</h2>
</div>
<a href="portfolio.html" class="btn btn-secondary fade-in">
<span>View All</span>
<i data-lucide="arrow-right" class="btn-icon"></i>
</a>
</div>
<!-- content -->
</div>
</section>
Pill Section (sage green background)
What I Do
Tools & Expertise
<section id="skills" class="skills-section">
<div class="container">
<div class="skills-header fade-in">
<p class="section-label">What I Do</p>
<h2 class="heading-1">Tools &<br />Expertise</h2>
</div>
<!-- content -->
</div>
</section>
/* .skills-section / .testimonials-section:
background: #E4F0EA · width: 94% · margin: 0 auto 80px · border-radius: 28px */
Footer
<footer class="site-footer">
<div class="container">
<!-- Large branding CTA -->
<div class="footer-branding-section fade-in">
<h2 class="footer-brand-heading">Let's Work<br />Together</h2>
<p class="footer-brand-subtext">Short availability line.</p>
<a href="contact.html" class="btn btn-primary footer-cta-btn">
<span>Get in Touch</span>
<i data-lucide="arrow-right" class="btn-icon"></i>
</a>
</div>
<!-- 3-column grid -->
<div class="footer-grid">
<div class="footer-col footer-nav">
<ul><li><a href="index.html">Home</a></li></ul>
</div>
<div class="footer-col footer-contact-col">
<h4 class="footer-heading">Status</h4>
<div class="footer-availability-status">
<span class="avail-dot"></span>
<span>Available for new roles</span>
</div>
<p class="footer-status-note">Based in NYC · Open to remote</p>
</div>
<div class="footer-col footer-ecosystem">
<h4 class="footer-heading">Ecosystem</h4>
<ul class="ecosystem-links">
<li><a href="#" target="_blank"><i data-lucide="linkedin"></i>LinkedIn</a></li>
</ul>
</div>
</div>
<!-- Bottom bar -->
<div class="footer-bottom-bar">
<div class="footer-legal"><a href="#">Privacy Policy</a></div>
<p class="footer-credit">Sazzad Hossain © 2026</p>
<button class="back-to-top" onclick="window.scrollTo({top:0,behavior:'smooth'})">
<i data-lucide="arrow-up"></i>
</button>
</div>
</div>
</footer>
Animations & States
All scroll reveals use .fade-in toggled by Intersection Observer. Hover patterns are consistent across all interactive surfaces.
Scroll Reveal
/* Add .fade-in to any element — JS adds .visible when it enters viewport */
.fade-in {
opacity: 0;
transform: translateY(15px);
transition: opacity 1s ease-out, transform 1s ease-out;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
/* JS in main.js */
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) e.target.classList.add('visible');
});
}, { threshold: 0.1 });
document.querySelectorAll('.fade-in').forEach(el => observer.observe(el));
Hover — Card Lift + Left Accent
/* Applied consistently to: portfolio items, testimonial cards, tool items */
.card:hover {
transform: translateY(-5px);
box-shadow: inset 4px 0 0 var(--color-primary),
0 20px 52px rgba(0, 0, 0, 0.08);
}
/* Arrow slide-in on portfolio items */
.portfolio-arrow {
opacity: 0;
transform: translateX(-6px);
transition: opacity 0.25s ease, transform 0.25s ease;
}
.portfolio-item:hover .portfolio-arrow {
opacity: 1;
transform: translateX(0);
}
Availability Dot Pulse
.avail-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #22c55e;
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.2);
animation: pulse-dot 2.5s ease-in-out infinite;
}
@keyframes pulse-dot {
0%, 100% { box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.2); }
50% { box-shadow: 0 0 0 7px rgba(34, 197, 94, 0.08); }
}
Marquee Strip
<div class="hero-marquee-wrap">
<div class="hero-marquee-inner">
<span>Product Design</span>
<span class="mq-sep">✦</span>
<span>UX Research</span>
<!-- duplicate content once for seamless loop -->
</div>
</div>
/* animation: heroMarquee 22s linear infinite → translateX(-50%)
Works by duplicating content so the loop is seamless */