├── .eleventy.js ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── netlify.toml ├── package-lock.json ├── package.json ├── postcss.config.js ├── scripts └── generate-formats.js ├── src ├── _data │ ├── fonts.js │ ├── site.js │ └── themes.js ├── _includes │ ├── base.njk │ ├── chapter.njk │ ├── chapterCard.njk │ ├── homepage.njk │ └── tracking.njk ├── assets │ ├── cover.png │ └── favicon.png ├── chapters │ ├── 01-chapter-one.md │ ├── 02-chapter-two.md │ ├── 03-chapter-three.md │ └── chapters.json ├── index.njk └── styles │ └── tailwind.css └── tailwind.config.js /.eleventy.js: -------------------------------------------------------------------------------- 1 | const eleventyNavigationPlugin = require("@11ty/eleventy-navigation"); 2 | 3 | module.exports = function(eleventyConfig) { 4 | 5 | // Navigation plugin 6 | eleventyConfig.addPlugin(eleventyNavigationPlugin); 7 | 8 | eleventyConfig.addPassthroughCopy("src/assets"); 9 | eleventyConfig.addPassthroughCopy("src/styles"); 10 | eleventyConfig.addPassthroughCopy("src/scripts"); 11 | eleventyConfig.addWatchTarget("./src/styles/tailwind.css"); 12 | 13 | eleventyConfig.addCollection("chapters", function(collectionApi) { 14 | return collectionApi.getFilteredByGlob("src/chapters/*.md").sort((a, b) => { 15 | return a.fileSlug.localeCompare(b.fileSlug, undefined, {numeric: true, sensitivity: 'base'}); 16 | }); 17 | }); 18 | 19 | eleventyConfig.addFilter("findNeighborChapters", function(collection, currentPage) { 20 | const currentIndex = collection.findIndex(page => page.url === currentPage.url); 21 | return { 22 | prev: collection[currentIndex - 1] || null, 23 | next: collection[currentIndex + 1] || null 24 | }; 25 | }); 26 | 27 | eleventyConfig.addFilter("dateFormat", function(date, format) { 28 | return new Date(date).toLocaleDateString('en-US', { 29 | year: format.includes('yyyy') ? 'numeric' : undefined, 30 | month: format.includes('MM') ? '2-digit' : undefined, 31 | day: format.includes('dd') ? '2-digit' : undefined 32 | }); 33 | }); 34 | 35 | // Add the pageTitle filter 36 | eleventyConfig.addFilter("pageTitle", function(title, siteTitle, author, isHomepage) { 37 | if (isHomepage) { 38 | // Homepage 39 | return `${siteTitle} by ${author}`; 40 | } else if (title) { 41 | // Chapter pages 42 | return `${title} - ${siteTitle}`; 43 | } 44 | return siteTitle; 45 | }); 46 | 47 | // Add this to change the permalinks for chapter files 48 | eleventyConfig.addGlobalData("eleventyComputed", { 49 | permalink: data => { 50 | // Check if it's a chapter file 51 | if (data.page.inputPath.includes("/chapters/")) { 52 | // Use the custom slug if provided, otherwise use the file slug 53 | const slug = data.customSlug || data.page.fileSlug; 54 | return `/${slug}/`; 55 | } 56 | // For non-chapter files, return null to use the default permalink 57 | return null; 58 | } 59 | }); 60 | 61 | return { 62 | dir: { 63 | input: "src", 64 | output: "dist" 65 | } 66 | }; 67 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | /dist/ 3 | 4 | # Dependency caches 5 | /node_modules/ 6 | 7 | # Operating system files 8 | .DS_Store 9 | Thumbs.db 10 | 11 | # Editor-specific files 12 | .idea/ 13 | *.iml 14 | .vscode/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.0.0] - 2024-07-14 9 | 10 | ### Added 11 | - Initial release of PutOut, an 11ty-based ebook-to-website conversion template 12 | - Chapter management system using Markdown files 13 | - Customizable themes with easy configuration 14 | - Responsive design for optimal viewing on various devices 15 | - SEO optimization with customizable meta tags 16 | - Social sharing capabilities 17 | - Hamburger menu for easy chapter navigation 18 | - Integration with Tailwind CSS for styling 19 | - Dynamic font loading based on Tailwind configuration 20 | - Easy deployment process, optimized for Netlify 21 | - Comprehensive README with setup and customization instructions 22 | - MIT License for open-source use and modification 23 | 24 | ### Features 25 | - Effortless setup process with clear documentation 26 | - Fast and lightweight static site generation using 11ty 27 | - Flexible theming system allowing for easy customization 28 | - Automatic generation of chapter-based navigation 29 | - SEO-friendly structure and meta tags 30 | - Social media integration for increased shareability 31 | 32 | [1.0.0]: https://github.com/deepakness/putout/releases/tag/v1.0.0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Deepak Kumar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PutOut - Ebook-to-Website with 11ty 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | [![Version](https://img.shields.io/github/package-json/v/deepakness/putout)](https://github.com/deepakness/putout) 5 | [![Last Commit](https://img.shields.io/github/last-commit/deepakness/putout)](https://github.com/deepakness/putout/commits/main) 6 | 7 | Turn your ebook into a beautiful, easy-to-navigate website using the power of [11ty](https://www.11ty.dev/), a simpler static site generator. This project provides a solid starting point with a pre-configured template, streamlined chapter management, and customizable themes. 8 | 9 | ## Table of Contents 10 | 11 | - [Quick Deploy](#quick-deploy-) 12 | - [Features](#features-) 13 | - [Demo](#demo-) 14 | - [Getting Started](#getting-started-) 15 | - [Deployment](#deployment-) 16 | - [Updating the Template](#updating-the-template-) 17 | - [Customization](#customization-) 18 | - [Contributing](#contributing-) 19 | - [Troubleshooting](#troubleshooting-) 20 | - [License](#license-) 21 | 22 | ## Quick Deploy ⚡️ 23 | 24 | Quickly deploy your own copy of this project to Netlify with one click: 25 | 26 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/deepakness/putout) 27 | 28 | This will create a new repository in your GitHub account with this project's files, connect it to Netlify, and deploy it. You can later make changes to the GitHub repository. 29 | 30 | ## Features ✨ 31 | 32 | - **Effortless Setup:** Get up and running quickly with a well-structured template and clear installation instructions. 33 | - **Chapter Organization:** Manage your ebook's chapters easily in individual Markdown files. 34 | - **Navigation Made Easy:** Automatic generation of a chapter-based navigation menu (hamburger menu on non-homepage pages). 35 | - **Customizable Themes:** Choose from multiple built-in themes to style your ebook website. 36 | - **Fast and Lightweight:** 11ty ensures a blazing-fast website experience for your readers. 37 | - **SEO-Friendly:** Meta tags for better search engine optimization are included. 38 | - **Social Links:** Make it easy for readers to find you on social media sites as well. 39 | - **PDF and EPUB Generation:** Automatically generate PDF and EPUB files for your ebook. 40 | 41 | ## Demo 🚀 42 | 43 | Check out the live demo of this ebook template: [Demo 1](https://ebook.untalkedseo.com/), [Demo 2](https://minimalism.putout.org/) 44 | 45 | ## Getting Started 🛠️ 46 | 47 | Check out [PutOut documentation](https://putout.org/docs/getting-started/) for more detailed information. 48 | 49 | 1. **Use This Template:** 50 | 51 | Click the "Use this template" button at the top of this repository to create your own copy. 52 | 53 | 2. **Clone Your Repository:** 54 | 55 | ```bash 56 | git clone https://github.com//.git 57 | ``` 58 | 59 | ```bash 60 | cd 61 | ``` 62 | 63 | 3. **Install Dependencies:** 64 | 65 | ```bash 66 | npm install 67 | ``` 68 | 69 | 4. **Configure Your Ebook:** 70 | 71 | - Open `src/_data/site.js` and customize the settings (title, author, description, social links, etc.) to match your ebook. 72 | - Replace the sample chapters in `src/chapters` with your ebook's chapters (in Markdown format). 73 | 74 | 5. **Start Development Server:** 75 | 76 | ```bash 77 | npm run start 78 | ``` 79 | This will start a local development server at `http://localhost:8080/`. Open this URL in your web browser. 80 | 81 | 6. **Build for Production:** 82 | 83 | ```bash 84 | npm run build 85 | ``` 86 | This will generate your static website files in the `dist/` directory, ready for deployment. 87 | 88 | ## Deployment 🚀 89 | 90 | The easiest way to deploy your ebook website is with [Netlify](https://www.netlify.com/): 91 | 92 | 1. Push your project to your GitHub repository. 93 | 2. Create a new site on Netlify and connect it to your GitHub repository. 94 | 3. Configure build settings (if needed): Set the build command to `npm run build` and the publish directory to `dist/`. 95 | 4. Deploy: Netlify will automatically build and deploy your site whenever you push changes to your repository. 96 | 97 | Learn more about [deployment](https://putout.org/docs/deployment/) 98 | 99 | ## Updating the Template 🔄 100 | 101 | To get the latest updates from the original template: 102 | 103 | 1. Add the original repository as a remote (you only need to do this once): 104 | 105 | ```bash 106 | git remote add template https://github.com/deepakness/putout.git 107 | ``` 108 | 109 | 2. Fetch the latest changes: 110 | 111 | ```bash 112 | git fetch template 113 | ``` 114 | 115 | 3. Merge the changes into your main branch: 116 | 117 | ```bash 118 | git merge template/main --allow-unrelated-histories 119 | ``` 120 | 121 | 4. Resolve any conflicts and commit the changes: 122 | 123 | ```bash 124 | git add . 125 | ``` 126 | 127 | ```bash 128 | git commit -m "Merged updates from template" 129 | ``` 130 | 131 | 5. Push the changes to your repository: 132 | 133 | ```bash 134 | git push origin main 135 | ``` 136 | 137 | ## Customization 🎨 138 | 139 | - **Themes:** Modify or add themes in `src/_data/themes.js`. 140 | - **Layouts:** Customize page layouts in `src/_includes/`. 141 | - **Styles:** Adjust styles by modifying `tailwind.config.js` and `src/styles/tailwind.css`. 142 | 143 | Learn more about [customization](https://putout.org/docs/customization/) 144 | 145 | ## Contributing 🤝 146 | 147 | Contributions are welcome! Please feel free to submit a Pull Request. 148 | 149 | 1. Fork the repository 150 | 2. Create your feature branch (`git checkout -b feature/AmazingFeature`) 151 | 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) 152 | 4. Push to the branch (`git push origin feature/AmazingFeature`) 153 | 5. Open a Pull Request 154 | 155 | ## Troubleshooting 🔧 156 | 157 | - **Build Errors:** Ensure all dependencies are installed (`npm install`) and you're using a compatible Node.js version. 158 | - **Styling Issues:** Check your `tailwind.config.js` and ensure you've rebuilt your CSS (`npm run build:css`). 159 | - **Content Not Updating:** Make sure your Markdown files are in the correct location and format. 160 | 161 | For more help, please [open an issue](https://github.com/deepakness/putout/issues). 162 | 163 | ## License 📄 164 | 165 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 166 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "dist" 3 | command = "npm run build" 4 | 5 | [dev] 6 | command = "npm run start" 7 | 8 | [functions] 9 | directory = "netlify/functions" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "putout", 3 | "version": "1.0.0", 4 | "description": "Turn your ebook into a beautiful, easy-to-navigate website using 11ty", 5 | "main": ".eleventy.js", 6 | "scripts": { 7 | "start": "npm-run-all --parallel watch:*", 8 | "watch:eleventy": "eleventy --serve", 9 | "watch:tailwind": "tailwindcss -i src/styles/tailwind.css -o dist/styles/tailwind.css --watch", 10 | "build": "npm-run-all build:*", 11 | "build:eleventy": "eleventy", 12 | "build:tailwind": "tailwindcss -i src/styles/tailwind.css -o dist/styles/tailwind.css", 13 | "build:formats": "node scripts/generate-formats.js" 14 | }, 15 | "keywords": [ 16 | "ebook", 17 | "11ty", 18 | "static-site-generator", 19 | "tailwindcss", 20 | "jamstack" 21 | ], 22 | "author": "DeepakNess (https://deepakness.com)", 23 | "license": "MIT", 24 | "dependencies": { 25 | "@11ty/eleventy": "^2.0.1", 26 | "@11ty/eleventy-navigation": "^0.3.5", 27 | "@tailwindcss/typography": "^0.5.13", 28 | "autoprefixer": "^10.4.19", 29 | "epub-gen": "^0.1.0", 30 | "lucide": "^0.473.0", 31 | "markdown-it": "^14.1.0", 32 | "postcss": "^8.4.39", 33 | "puppeteer": "^22.15.0", 34 | "tailwindcss": "^3.4.4" 35 | }, 36 | "devDependencies": { 37 | "npm-run-all": "^4.1.5" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "git+https://github.com/deepakness/putout.git" 42 | }, 43 | "bugs": { 44 | "url": "https://github.com/deepakness/putout/issues" 45 | }, 46 | "homepage": "https://github.com/deepakness/putout#readme", 47 | "engines": { 48 | "node": ">=14.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | } 6 | } -------------------------------------------------------------------------------- /scripts/generate-formats.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | const Epub = require('epub-gen'); 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const MarkdownIt = require('markdown-it'); 6 | const site = require('../src/_data/site.js'); 7 | 8 | const md = new MarkdownIt(); 9 | 10 | async function getChapterContent() { 11 | const chaptersDir = path.join(__dirname, '../src/chapters'); 12 | const files = fs.readdirSync(chaptersDir) 13 | .filter(file => file.endsWith('.md')) 14 | .sort((a, b) => { 15 | const numA = parseInt(a.match(/\d+/)[0]); 16 | const numB = parseInt(b.match(/\d+/)[0]); 17 | return numA - numB; 18 | }); 19 | 20 | const chapters = []; 21 | for (const file of files) { 22 | const content = fs.readFileSync(path.join(chaptersDir, file), 'utf-8'); 23 | 24 | // Extract title from frontmatter 25 | const titleMatch = content.match(/title:\s*"([^"]+)"/); 26 | const title = titleMatch ? titleMatch[1] : ''; 27 | 28 | // Remove frontmatter (content between --- markers) 29 | const cleanContent = content.replace(/^---[\s\S]*?---\n/, ''); 30 | 31 | // Convert markdown to HTML 32 | const htmlContent = md.render(cleanContent); 33 | 34 | chapters.push({ 35 | title: title, 36 | data: htmlContent 37 | }); 38 | } 39 | return chapters; 40 | } 41 | 42 | async function generatePDF() { 43 | if (!site.formats.pdf) return; 44 | 45 | const browser = await puppeteer.launch({ headless: 'new' }); 46 | const page = await browser.newPage(); 47 | 48 | // Create a temporary HTML file with all chapters 49 | const chapters = await getChapterContent(); 50 | const html = ` 51 | 52 | 53 | 54 | ${site.title} 55 | 118 | 119 | 120 |

${site.longTitle || site.title}

121 | ${chapters.map(chapter => ` 122 |
123 |

${chapter.title}

124 | ${chapter.data} 125 |
126 | `).join('')} 127 | 128 | 129 | `; 130 | 131 | const tempHtmlPath = path.join(__dirname, '../dist/temp.html'); 132 | fs.writeFileSync(tempHtmlPath, html); 133 | 134 | await page.goto(`file://${tempHtmlPath}`); 135 | await page.pdf({ 136 | path: path.join(__dirname, '../dist/book.pdf'), 137 | format: 'A4', 138 | margin: { top: '2.5cm', right: '2.5cm', bottom: '2.5cm', left: '2.5cm' }, 139 | printBackground: true 140 | }); 141 | 142 | fs.unlinkSync(tempHtmlPath); 143 | await browser.close(); 144 | } 145 | 146 | async function generateEPUB() { 147 | if (!site.formats.epub) return; 148 | 149 | const chapters = await getChapterContent(); 150 | const coverPath = path.join(__dirname, '../src', site.coverImage); 151 | 152 | // Check if cover image exists 153 | if (!fs.existsSync(coverPath)) { 154 | console.warn('Warning: Cover image not found at', coverPath); 155 | } 156 | 157 | const options = { 158 | title: site.title, 159 | author: site.author, 160 | publisher: site.author, 161 | cover: coverPath, 162 | content: chapters.map(chapter => ({ 163 | title: chapter.title, 164 | data: chapter.data, 165 | beforeToc: chapter === chapters[0] // Only first chapter appears before TOC 166 | })), 167 | css: ` 168 | body { 169 | font-family: 'Merriweather', serif; 170 | line-height: 1.8; 171 | font-size: 1.1em; 172 | } 173 | h1 { 174 | text-align: center; 175 | font-size: 2em; 176 | margin: 1em 0; 177 | line-height: 1.2; 178 | } 179 | h2 { 180 | text-align: center; 181 | font-size: 1.5em; 182 | margin: 1em 0; 183 | line-height: 1.3; 184 | } 185 | p { 186 | margin: 1em 0; 187 | text-align: justify; 188 | } 189 | .chapter { 190 | margin: 2em 0; 191 | } 192 | ` 193 | }; 194 | 195 | await new Epub(options, path.join(__dirname, '../dist/book.epub')).promise; 196 | } 197 | 198 | async function main() { 199 | try { 200 | const distDir = path.join(__dirname, '../dist'); 201 | // Ensure dist directory exists 202 | if (!fs.existsSync(distDir)) { 203 | fs.mkdirSync(distDir, { recursive: true }); 204 | } 205 | 206 | await generatePDF(); 207 | await generateEPUB(); 208 | console.log('Book formats generated successfully!'); 209 | } catch (error) { 210 | console.error('Error generating book formats:', error); 211 | process.exit(1); 212 | } 213 | } 214 | 215 | main(); -------------------------------------------------------------------------------- /src/_data/fonts.js: -------------------------------------------------------------------------------- 1 | const tailwindConfig = require('../../tailwind.config.js'); 2 | 3 | function generateGoogleFontsUrl() { 4 | const { theme } = tailwindConfig; 5 | const sansFont = theme.extend.fontFamily.sans[0]; 6 | const serifFont = theme.extend.fontFamily.serif[0]; 7 | 8 | const fonts = [ 9 | `family=${sansFont.replace(/\s+/g, '+')}:wght@400;600;700`, 10 | `family=${serifFont.replace(/\s+/g, '+')}:ital,wght@0,400;0,700;1,400` 11 | ]; 12 | 13 | return `https://fonts.googleapis.com/css2?${fonts.join('&')}&display=swap`; 14 | } 15 | 16 | module.exports = generateGoogleFontsUrl(); -------------------------------------------------------------------------------- /src/_data/site.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file makes your book title, cover, theme, social media links, and website. 3 | 4 | Comments (like this one or that starts with //) will help you know what a line of code does. 5 | Read the comment and make changes accordingly. 6 | 7 | You don't need to change these comment lines unless you are modifying the setting itself. 8 | */ 9 | 10 | module.exports = { 11 | // Main site details 12 | title: "The Art of Baking", // Short title of the book 13 | longTitle: "The Ultimate Guide to Becoming a Baking Master", // Long title of the book (if not, keep the same as "title") 14 | author: "Chef Baker", // Author's name 15 | description: "A comprehensive journey from novice to expert, covering essential techniques, delicious recipes, and expert tips.", // Brief description of the book 16 | url: "https://putout.org", // The exact domain or subdomain where you'll be hosting 17 | coverImage: "/assets/cover.png", // Address for the cover image (relative to src directory) 18 | 19 | // Theme selection 20 | theme: "default", // Options: "default", "glass", "dark", "purple", "warm" 21 | 22 | // Book format options 23 | formats: { 24 | pdf: true, // Set to true to enable PDF generation 25 | epub: true // Set to true to enable EPUB generation 26 | }, 27 | 28 | // Social media links (optional) – remove '//' from infront of them to make them active 29 | socialLinks: [ 30 | { platform: "twitter", url: "https://twitter.com/yourusername" }, 31 | { platform: "github", url: "https://github.com/yourusername" }, 32 | // { platform: "linkedin", url: "https://linkedin.com/in/yourusername" }, 33 | { platform: "website", url: "https://putout.org/" } 34 | ] 35 | }; -------------------------------------------------------------------------------- /src/_data/themes.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file all existing themes and also lets you create new themes. 3 | 4 | Comments (like this one or that starts with //) will help you know what a line of code does. 5 | Read the comment and make changes accordingly. 6 | 7 | You don't need to change these comment lines unless you are modifying the setting itself. 8 | */ 9 | 10 | module.exports = { 11 | default: { 12 | body: 'bg-white text-gray-900', 13 | header: 'bg-gray-100 text-gray-900', 14 | card: 'bg-gray-50 border border-gray-200 shadow-sm', 15 | button: 'bg-gray-200 text-gray-900 hover:bg-gray-300 border border-gray-300', 16 | prose: 'prose', 17 | icons: 'text-gray-500 hover:text-gray-900 transition-colors duration-300' 18 | }, 19 | glass: { 20 | body: 'bg-gradient-to-br from-blue-500 to-purple-600 text-white bg-fixed', 21 | header: 'bg-white bg-opacity-10 backdrop-blur-md border-b border-white border-opacity-20', 22 | card: 'bg-white bg-opacity-10 backdrop-blur-md border border-white border-opacity-20 shadow-lg', 23 | button: 'bg-white bg-opacity-20 backdrop-blur-md border border-white border-opacity-20 text-white hover:bg-opacity-30', 24 | prose: 'prose prose-invert', 25 | icons: 'text-gray-300 hover:text-white transition-colors duration-300' 26 | }, 27 | dark: { 28 | body: 'bg-gray-900 text-gray-100', 29 | header: 'bg-gray-800 text-gray-100', 30 | card: 'bg-gray-800 border border-gray-700 shadow-md', 31 | button: 'bg-gray-700 text-gray-100 hover:bg-gray-600 border border-gray-600', 32 | prose: 'prose prose-invert', 33 | icons: 'text-gray-400 hover:text-gray-200 transition-colors duration-300' 34 | }, 35 | purple: { 36 | body: 'bg-white text-gray-900', 37 | header: 'bg-purple-100 text-gray-900', 38 | card: 'bg-purple-50 border border-purple-200 shadow-sm', 39 | button: 'bg-purple-100 text-gray-900 hover:bg-purple-200 border border-purple-300', 40 | prose: 'prose', 41 | icons: 'text-purple-400 hover:text-purple-900 transition-colors duration-300' 42 | }, 43 | warm: { 44 | body: 'bg-orange-50 text-orange-900', 45 | header: 'bg-orange-100 text-orange-900', 46 | card: 'bg-white border border-orange-200 shadow-sm', 47 | button: 'bg-orange-200 text-orange-900 hover:bg-orange-300 border border-orange-300', 48 | prose: 'prose prose-orange font-serif text-lg', 49 | icons: 'text-orange-400 hover:text-orange-900 transition-colors duration-300' 50 | }, 51 | }; -------------------------------------------------------------------------------- /src/_includes/base.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title | pageTitle(site.title, site.author, isHomepage) }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {% include "tracking.njk" %} 38 | 39 | 40 | 41 | 46 | 47 | 48 | 49 | {% if not isHomepage %} 50 | 62 | 63 | 64 | 65 | 66 | 67 | 72 | {% endif %} 73 | 74 |
75 |
76 | {{ site.title }} 77 |
78 |
79 |
80 | {{ content | safe }} 81 |
82 | 83 | 125 | 126 | -------------------------------------------------------------------------------- /src/_includes/chapter.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base.njk 3 | --- 4 | -------------------------------------------------------------------------------- /src/_includes/chapterCard.njk: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ chapter.data.title }}

4 |

{{ chapter.data.description }}

5 | Read Chapter 6 |
7 |
-------------------------------------------------------------------------------- /src/_includes/homepage.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base.njk 3 | --- 4 |
5 |
6 | 7 |
8 | {{ site.title }} cover 9 |

{{ site.longTitle }}

10 |

by {{ site.author }}

11 |

{{ site.description }}

12 |
13 | {% if site.formats.pdf %} 14 | 15 | 16 | 17 | {% endif %} 18 | {% if site.formats.epub %} 19 | 20 | 21 | 22 | {% endif %} 23 | {% for link in site.socialLinks %} 24 | {% if link.url %} 25 | 26 | {% if link.platform == "twitter" %} 27 | 28 | {% elif link.platform == "github" %} 29 | 30 | {% elif link.platform == "linkedin" %} 31 | 32 | {% else %} 33 | 34 | {% endif %} 35 | 36 | {% endif %} 37 | {% endfor %} 38 |
39 |
40 | 41 |
42 |
43 | {%- for chapter in collections.chapters -%} 44 | {% include "chapterCard.njk" %} 45 | {%- endfor -%} 46 |
47 |
48 |
49 |
-------------------------------------------------------------------------------- /src/_includes/tracking.njk: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakness/putout/d8aede1fa879a1b88c9646efa27fa626fc3c6689/src/assets/cover.png -------------------------------------------------------------------------------- /src/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakness/putout/d8aede1fa879a1b88c9646efa27fa626fc3c6689/src/assets/favicon.png -------------------------------------------------------------------------------- /src/chapters/01-chapter-one.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction to Baking" 3 | description: "Laying the groundwork for a lifetime of delicious creations, we'll cover the fundamentals of baking." 4 | customSlug: "introduction" 5 | --- 6 | 7 | Baking is the magical process of transforming simple ingredients like flour, sugar, eggs, and butter into mouthwatering treats. It's a science that requires precision and an art that allows for creativity. 8 | 9 | ## Why is Baking Important? 10 | 11 | Baking is more than just following recipes; it's about creating joy, sharing with loved ones, and expressing yourself through culinary creations. From the aroma that fills your kitchen to the first bite of a warm cookie, baking brings happiness to life's everyday moments. 12 | 13 | ## Key Concepts 14 | 15 | - **Ingredients:** Understanding the role of each ingredient is crucial for successful baking. 16 | - **Measurements:** Precision is key! We'll explore different measurement systems and techniques. 17 | - **Techniques:** Learn the essential techniques like mixing, kneading, and proofing that form the foundation of baking. -------------------------------------------------------------------------------- /src/chapters/02-chapter-two.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Essential Baking Equipments" 3 | description: "Discover the must-have tools and equipment that will set you up for baking success." 4 | customSlug: "essential-equipment" 5 | --- 6 | 7 | Before you embark on your baking journey, it's important to gather the essential tools that will help you achieve delicious results. While you don't need a professional kitchen, having the right equipment on hand will make the process smoother and more enjoyable. 8 | 9 | ## Must-Have Baking Tools 10 | 11 | Here are some indispensable tools every baker should own: 12 | 13 | **1. Measuring Cups and Spoons:** Accurate measurements are crucial in baking, as even slight variations can significantly impact the outcome of your recipe. Invest in a set of both dry and liquid measuring cups and spoons for precise measuring. 14 | 15 | **2. Mixing Bowls:** A set of nesting mixing bowls in various sizes is essential for combining ingredients, whipping egg whites, and creating batters. Choose bowls made from durable materials like stainless steel or glass. 16 | 17 | **3. Whisks, Spatulas, and Rubber Scrapers:** These versatile tools are your allies in achieving smooth batters and evenly incorporated ingredients. A whisk is perfect for incorporating air into egg whites or cream, while spatulas and rubber scrapers help you scrape every last bit of batter from the bowl. 18 | 19 | **4. Baking Sheets and Pans:** From cookies to cakes, you'll need a variety of baking sheets and pans in different sizes and shapes. Look for nonstick options for easy release and cleanup. 20 | 21 | **5. Parchment Paper and Silicone Baking Mats:** Prevent your baked goods from sticking to baking sheets by lining them with parchment paper or silicone baking mats. These reusable mats also promote even browning. 22 | 23 | **6. Oven Thermometer:** Don't rely solely on your oven's temperature gauge. An oven thermometer provides an accurate reading of your oven's internal temperature, ensuring your baked goods cook evenly. 24 | 25 | **7. Cooling Rack:** Allow your baked goods to cool completely before frosting or storing them. A cooling rack allows air to circulate around your treats, preventing condensation and sogginess. 26 | 27 | ## Nice-to-Have Tools 28 | 29 | While not strictly essential, these tools can enhance your baking experience and open up new possibilities: 30 | 31 | **1. Stand Mixer:** A stand mixer is a worthwhile investment for serious bakers. It tackles heavy mixing tasks with ease, making light work of kneading doughs and whipping creams. 32 | 33 | **2. Handheld Mixer:** A handheld mixer is a more affordable option for occasional bakers, ideal for whipping cream or beating eggs. 34 | 35 | **3. Food Processor:** A food processor can be helpful for quickly pulsing together pie crusts or grinding nuts. 36 | 37 | **4. Pastry Blender:** A pastry blender is designed to cut cold butter into flour, creating flaky pie crusts and biscuits. 38 | 39 | **5. Rolling Pin:** A rolling pin is essential for rolling out dough for pie crusts, cookies, and pastries. 40 | 41 | ## Caring for Your Baking Tools 42 | 43 | Proper care and cleaning will extend the life of your baking tools. Wash them thoroughly with warm, soapy water after each use, and dry them completely to prevent rust. Store your tools in a dry, organized space for easy access. -------------------------------------------------------------------------------- /src/chapters/03-chapter-three.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Basic Baking Techniques" 3 | description: "Learn the fundamental techniques that will transform you into a confident baker." 4 | customSlug: "basic-techniques" 5 | --- 6 | 7 | Mastering basic baking techniques is the key to consistent, delicious results. These fundamental skills will become second nature as you gain experience, allowing you to confidently tackle any recipe. 8 | 9 | ## Essential Baking Techniques 10 | 11 | **1. Measuring Dry Ingredients (Like a Pro):** 12 | 13 | - **Spoon & Level Method:** Use a spoon to lightly scoop your dry ingredient (flour, sugar, etc.) into the measuring cup. Do not pack it down. Use a straight edge (like a knife or spatula) to level off the excess. 14 | - **Weight Measurements:** For the most accurate measurements, especially when working with flour, use a kitchen scale. 15 | 16 | **2. Measuring Liquid Ingredients:** 17 | 18 | - Use a liquid measuring cup (usually glass or clear plastic with a spout) and place it on a flat surface. 19 | - Pour the liquid into the cup until it reaches the desired measurement line. 20 | 21 | **3. Creaming Butter and Sugar:** 22 | 23 | - This technique creates light and airy cakes and cookies. 24 | - Use softened butter (not melted). 25 | - Beat the butter and sugar together using a mixer until the mixture is light and fluffy and pale in color. 26 | 27 | **4. Beating Egg Whites:** 28 | 29 | - Start with clean, dry beaters and a clean bowl (any fat will prevent the whites from whipping properly). 30 | - Beat the egg whites on medium speed until they are foamy. 31 | - Gradually add sugar (if the recipe calls for it), beating until stiff peaks form. 32 | 33 | **5. Folding Ingredients:** 34 | 35 | - This gentle technique is used to combine delicate ingredients like whipped egg whites or whipped cream without deflating them. 36 | - Use a rubber spatula and a light hand. 37 | - Cut down through the center of the mixture and gently lift and turn the spatula, bringing the bottom mixture to the top. Rotate the bowl and repeat until just combined. 38 | 39 | **6. Kneading Dough:** 40 | 41 | - Kneading develops gluten, which gives bread its structure. 42 | - Place the dough on a lightly floured surface. 43 | - Use the heels of your hands to push the dough away from you, then fold it back over itself. 44 | - Rotate the dough and repeat for the amount of time specified in the recipe. 45 | 46 | **7. Understanding Oven Temperatures:** 47 | 48 | - **Baking:** Surrounds food with dry, hot air, resulting in browning and a crispier texture. 49 | - **Broiling:** Uses direct, high heat from the top of the oven for browning and crisping the surface of food. 50 | 51 | ## Practice Makes Perfect 52 | 53 | Don't be discouraged if your first attempts aren't perfect. Baking is a journey of learning and experimentation. The more you practice these basic techniques, the more confident and skilled you'll become. So embrace the process, have fun, and enjoy the delicious rewards of your baking adventures! -------------------------------------------------------------------------------- /src/chapters/chapters.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": "chapter.njk", 3 | "tags": "post" 4 | } -------------------------------------------------------------------------------- /src/index.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: homepage.njk 3 | isHomepage: true 4 | --- 5 | {{ site.title }} cover 6 |

{{ site.description }}

7 | 8 |
9 | {%- for chapter in collections.chapters -%} 10 | {% include "chapterCard.njk" %} 11 | {%- endfor -%} 12 |
-------------------------------------------------------------------------------- /src/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* Lucide icon styles */ 6 | [data-lucide] { 7 | width: 24px; 8 | height: 24px; 9 | stroke-width: 1.5; 10 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const defaultTheme = require('tailwindcss/defaultTheme') 2 | 3 | module.exports = { 4 | content: ["./src/**/*.{html,js,njk,md}"], 5 | theme: { 6 | extend: { 7 | fontFamily: { 8 | serif: ['Merriweather', ...defaultTheme.fontFamily.serif], 9 | sans: ['Inter', ...defaultTheme.fontFamily.sans], 10 | }, 11 | }, 12 | }, 13 | plugins: [ 14 | require('@tailwindcss/typography'), 15 | ], 16 | } --------------------------------------------------------------------------------