Skip to content

About SoapCraft Academy

SoapCraft Academy is a minimalist, safety-first training space for home soap makers. We teach the science and artistry behind cold and hot process soaps through clear, practical lessons.

Today’s focus
A clear craft is a safe craft: disciplined recipes, honest labeling, and sustainable choices.

Our Mission

Empower anyone, anywhere to craft safe, sustainable soaps with professional clarity and zero fluff.

Safety-first
PPE, accurate lye handling, and batch documentation as non-negotiables.
Skill clarity
Step-by-step methods with decision points explained, not hidden.
Responsible impact
Thoughtful sourcing and disposal that respects people and water systems.

Our Story

Started by instructors who balanced lab precision with studio creativity, we designed courses that fit any schedule, support assistive technologies, and respect regional ingredient availability.

What “minimalist training” means in practice

We remove distractions and focus on repeatable outcomes: accurate calculation, controlled trace, cure tracking, and ingredient literacy.

We teach how to evaluate claims and myths, so you can make decisions with confidence (and document them responsibly).

Global-first approach

Recipes and techniques are explained with substitution logic for regional availability and climate differences.

We provide metric and imperial notes, and we avoid “only one supplier” assumptions in guidance.

Values

Truthful labeling
We advocate for plain-language claims and traceable ingredients.
Community respect
Different methods can be valid; we emphasize safety and documentation over dogma.
Measurable learning
If it can’t be tested, it’s not taught as fact. We show how to verify results.

Team

Minimalism & Accessibility

Accessibility notes

We aim for clear hierarchy, low cognitive load, and predictable controls. This page supports keyboard navigation, prefers reduced motion, and keeps interactive elements labeled.

If you rely on assistive tech and something isn’t working, you can use the contact modal to report the issue.

Sustainability

We encourage efficient batching, mindful packaging, and safe disposal practices that protect waterways.

How we think about impact

Ingredient choices depend on region, budgets, and availability. We prioritize responsible sourcing and transparent trade-offs over perfectionism.

We also promote accurate cure times and storage practices to reduce waste from failed batches.

Ethical Craft Charter

We endorse responsible sourcing, safe disposal, and honest labeling.

Interactive charter
Charter is currently: Collapsed
dark:text-slate-50">Outcome: learners can substitute responsibly and still hit hardness, lather, and conditioning targets.

', '' ].join('') }, 3: { title: 'Phase 3 — Global Readiness', body: [ '
', '

Goal: make the material work across regions and constraints.

', '

What changed: we added unit flexibility, sourcing references, and troubleshooting pathways.

', '

Outcome: learners adapt, document, and improve — even with different ingredients and climates.

', '
' ].join('') } }; const p = map[phase] || map[1]; openModal({ kicker: 'Timeline', title: p.title, bodyHtml: p.body, showContactForm: false }); } function prefersReducedMotion() { return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } function setAutoplay(on) { state.timeline.autoplay = !!on; document.getElementById('tlAutoplay').textContent = state.timeline.autoplay ? 'on' : 'off'; } function autoplayStep() { const order = [1,2,3]; const idx = Math.max(0, order.indexOf(state.timeline.focus)); const next = order[(idx + 1) % order.length]; collapseAllTimeline(); toggleTimeline(next, true); } function startTimelineAutoplay() { if (state.timeline.timerId) clearInterval(state.timeline.timerId); setAutoplay(true); toggleTimeline(String(state.timeline.focus), true); state.timeline.timerId = setInterval(() => { if (!state.timeline.autoplay) return; autoplayStep(); }, 5200); } function stopTimelineAutoplay() { setAutoplay(false); } function wireUI() { document.querySelectorAll('[data-tl]').forEach(btn => { btn.addEventListener('click', () => { const id = btn.getAttribute('data-tl'); const el = document.getElementById('tl-' + id); const willOpen = el?.classList.contains('hidden'); if (willOpen) { collapseAllTimeline(); toggleTimeline(id, true); } else { toggleTimeline(id, false); } }); }); document.querySelectorAll('[data-openphase]').forEach(b => { b.addEventListener('click', () => openPhaseModal(Number(b.getAttribute('data-openphase')))); }); document.querySelectorAll('[data-nextphase]').forEach(b => { b.addEventListener('click', () => { const n = Number(b.getAttribute('data-nextphase')); collapseAllTimeline(); toggleTimeline(String(n), true); }); }); document.getElementById('tlRewind')?.addEventListener('click', () => { collapseAllTimeline(); toggleTimeline('1', true); }); document.getElementById('tlCollapseAll')?.addEventListener('click', () => collapseAllTimeline()); document.getElementById('tlPlay')?.addEventListener('click', () => startTimelineAutoplay()); document.getElementById('tlPause')?.addEventListener('click', () => stopTimelineAutoplay()); document.getElementById('pauseCounter')?.addEventListener('click', (e) => { const btn = e.currentTarget; state.counter.running = !state.counter.running; btn.textContent = state.counter.running ? 'Pause' : 'Resume'; if (!state.counter.running) stopCounter(); }); document.getElementById('resetCounter')?.addEventListener('click', () => resetCounter()); document.getElementById('openHelpModal')?.addEventListener('click', () => { openModal({ kicker: 'Help', title: 'How the timeline works', bodyHtml: [ '
', '

Use Expand to open a phase. Opening one phase automatically collapses the others to keep the page readable.

', '

Play cycles through the phases every few seconds. Pause stops autoplay (your manual expands still work).

', '

The learners counter updates locally without analytics or network calls.

', '
' ].join(''), showContactForm: false }); }); document.getElementById('openStoryModal')?.addEventListener('click', () => { openModal({ kicker: 'Manifesto', title: 'The SoapCraft promise', bodyHtml: [ '
', '

We teach soap making like a craft you can repeat, not a trick you try once.

', '

Every technique must pass three tests: safety, clarity, and results.

', '

If a step can’t be explained, measured, or verified, we don’t teach it.

', '
' ].join(''), showContactForm: false }); }); document.getElementById('openContactModal')?.addEventListener('click', () => { openModal({ kicker: 'Contact', title: 'Send a note to the team', bodyHtml: '

We read every message. If you include your batch size and process (cold/hot), we can reply with sharper guidance.

', showContactForm: true }); }); document.getElementById('modalClose')?.addEventListener('click', () => closeModal()); document.getElementById('modalOk')?.addEventListener('click', () => closeModal()); document.getElementById('contactCancel')?.addEventListener('click', () => closeModal()); document.getElementById('modalOverlay')?.addEventListener('click', (e) => { if (e.target === e.currentTarget) closeModal(); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); }); document.getElementById('contactForm')?.addEventListener('submit', (e) => { e.preventDefault(); const ok = validateContactForm(); if (!ok) return; const submitBtn = document.getElementById('contactSubmit'); submitBtn.disabled = true; submitBtn.textContent = 'Sending...'; setTimeout(() => { submitBtn.disabled = false; submitBtn.textContent = 'Send message'; document.getElementById('contactSuccess').classList.remove('hidden'); }, 700); }); } function init() { wireUI(); injectPartials(); startCounter(); collapseAllTimeline(); toggleTimeline('1', true); setAutoplay(false); } init(); })();