master fc0c8a4a093f cached
70 files
155.6 KB
42.8k tokens
10 symbols
1 requests
Download .txt
Repository: joshuacc/prose-for-programmers
Branch: master
Commit: fc0c8a4a093f
Files: 70
Total size: 155.6 KB

Directory structure:
gitextract_xvvrlvj7/

├── .eleventy.js
├── .eleventyignore
├── .forestry/
│   └── settings.yml
├── .gitattributes
├── .gitignore
├── .nvmrc
├── 404.md
├── LICENSE
├── LICENSE.txt
├── README.md
├── _data/
│   └── site.json
├── _includes/
│   ├── assets/
│   │   ├── css/
│   │   │   ├── 404.css
│   │   │   └── inline.css
│   │   ├── js/
│   │   │   ├── 404.js
│   │   │   ├── inline.js
│   │   │   └── search.js
│   │   └── utils/
│   │       ├── image.txt
│   │       └── imagesize.txt
│   ├── components/
│   │   ├── contact.njk
│   │   ├── footer.njk
│   │   ├── head.njk
│   │   ├── header.njk
│   │   └── nav.njk
│   ├── experimental/
│   │   └── blog/
│   │       ├── author.njk
│   │       ├── authors.njk
│   │       ├── components/
│   │       │   ├── pageslist.njk
│   │       │   └── postslist.njk
│   │       ├── content/
│   │       │   └── posts/
│   │       │       ├── firstpost.md
│   │       │       ├── fourthpost.md
│   │       │       ├── posts.json
│   │       │       ├── secondpost.md
│   │       │       ├── the-fifth-and-hopefully-final-example-post.md
│   │       │       └── thirdpost.md
│   │       ├── layouts/
│   │       │   ├── blog.njk
│   │       │   └── post.njk
│   │       ├── pages/
│   │       │   └── blog.md
│   │       └── tags.njk
│   └── layouts/
│       ├── 404.njk
│       ├── base.njk
│       ├── contact.njk
│       ├── page.njk
│       └── robots.njk
├── admin/
│   ├── config.yml
│   ├── index.html
│   └── preview-templates/
│       ├── index.js
│       ├── page.js
│       └── post.js
├── content/
│   ├── buy.md
│   └── pages/
│       ├── contact.md
│       ├── home.md
│       └── pages.json
├── filters/
│   └── searchFilter.js
├── loader.js
├── manuscript/
│   ├── Book.txt
│   ├── manuscript.json
│   ├── process.json
│   ├── process.md
│   ├── rules.json
│   ├── rules.md
│   ├── why.json
│   └── why.md
├── netlify.toml
├── outline.md
├── package.json
├── password_template.html
├── postcss.config.js
├── robots.md
├── search-index.json.njk
└── styles/
    ├── tailwind.config.js
    └── tailwind.css

================================================
FILE CONTENTS
================================================

================================================
FILE: .eleventy.js
================================================
const { DateTime } = require("luxon");
const CleanCSS = require("clean-css");
const UglifyJS = require("uglify-es");
const htmlmin = require("html-minifier");
const svgContents = require("eleventy-plugin-svg-contents");
const mdIterator = require('markdown-it-for-inline')
const embedEverything = require("eleventy-plugin-embed-everything");
const pluginTOC = require('eleventy-plugin-nesting-toc');
const eleventyNavigationPlugin = require("@11ty/eleventy-navigation");
const Image = require("@11ty/eleventy-img");
const fs = require('fs');
module.exports = function(eleventyConfig) {
  // eleventyConfig.addPlugin(pluginTOC);
  eleventyConfig.addPlugin(svgContents); 
  eleventyConfig.addPlugin(embedEverything);
  eleventyConfig.addShortcode("version", function () {
    return String(Date.now());
  });

  // Responsive image shortcode
  eleventyConfig.addLiquidShortcode("image", async function(src, alt, sizes = "100vw") {
    if(alt === undefined) {
      // You bet we throw an error on missing alt (alt="" works okay)
      throw new Error(`Missing \`alt\` on responsiveimage from: ${src}`);
    }
    src = './content/images/'+src
    let metadata = await Image(src, {
      widths: [400, 600, 800, 1000, 1200, 1400, 1600, 1900],
      formats: ['webp', 'jpeg', 'png'],
      urlPath: "/content/images/",
      outputDir: "./_site/content/images/"
    });

    let lowsrc = metadata.jpeg[0];

    let picture = `<picture>
      ${Object.values(metadata).map(imageFormat => {
        return `  <source type="image/${imageFormat[0].format}" srcset="${imageFormat.map(entry => entry.srcset).join(", ")}" sizes="${sizes}">`;
      }).join("\n")}
        <img
          data-src="${lowsrc.url}"
          width="${lowsrc.width}"
          height="${lowsrc.height}"
          alt="${alt}">
      </picture>`;

      return `${picture}`;

  });

  eleventyConfig.addLiquidShortcode("icon", function(title,url) {
    return '<img class="icon" src="'+url+'" alt="'+title+'" />';
  });

  // Button shortcode -- experimental
  // eleventyConfig.addLiquidShortcode("button", function(title,url) {
  //   return '<a class="button" href="'+url+'">'+title+'</a>';
  // });


  // Tailwind pass through and watch target
  eleventyConfig.addWatchTarget("./_tmp/style.css");
  eleventyConfig.addPassthroughCopy({ "./_tmp/style.css": "./style.css" });

  // Alpine.js pass through
  eleventyConfig.addPassthroughCopy({
    "./node_modules/alpinejs/dist/alpine.js": "./js/alpine.js",
  });

  // Eleventy Navigation https://www.11ty.dev/docs/plugins/navigation/
  eleventyConfig.addPlugin(eleventyNavigationPlugin);

  // Configuration API: use eleventyConfig.addLayoutAlias(from, to) to add
  // layout aliases! Say you have a bunch of existing content using
  // layout: post. If you don’t want to rewrite all of those values, just map
  // post to a new file like this:
  // eleventyConfig.addLayoutAlias("post", "layouts/my_new_post_layout.njk");

  // Merge data instead of overriding
  // https://www.11ty.dev/docs/data-deep-merge/
  eleventyConfig.setDataDeepMerge(true);

  // Add support for maintenance-free post authors
  // Adds an authors collection using the author key in our post frontmatter
  // Thanks to @pdehaan: https://github.com/pdehaan
  // eleventyConfig.addCollection("authors", collection => {
  //   const blogs = collection.getFilteredByGlob("posts/*.md");
  //   return blogs.reduce((coll, post) => {
  //     const author = post.data.author;
  //     if (!author) {
  //       return coll;
  //     }
  //     if (!coll.hasOwnProperty(author)) {
  //       coll[author] = [];
  //     }
  //     coll[author].push(post.data);
  //     return coll;
  //   }, {});
  // });

   // Creates custom collection "pages"
   eleventyConfig.addCollection("pages", function(collection) {
    return collection.getFilteredByGlob("pages/*.md");
   });

   // Creates custom collection "posts"
  //  eleventyConfig.addCollection("posts", function(collection) {
  //   const coll = collection.getFilteredByGlob("posts/*.md");
  
  //   for(let i = 0; i < coll.length ; i++) {
  //     const prevPost = coll[i-1];
  //     const nextPost = coll[i + 1];
  
  //     coll[i].data["prevPost"] = prevPost;
  //     coll[i].data["nextPost"] = nextPost;
  //   }
  
  //   return coll;
  // });
    

   // Creates custom collection "results" for search
   const searchFilter = require("./filters/searchFilter");
   eleventyConfig.addFilter("search", searchFilter);
   eleventyConfig.addCollection("results", collection => {
    return [...collection.getFilteredByGlob("**/*.md")];
   });
  
   // Creates custom collection "menuItems"
  eleventyConfig.addCollection("menuItems", collection => {
    const coll = collection
    .getFilteredByGlob("manuscript/*.md")
    .sort((a, b) => {
        return (a.data.eleventyNavigation.order || 0) - (b.data.eleventyNavigation.order || 0);
      })
    return coll;
  });

  // Date formatting (human readable)
  eleventyConfig.addFilter("readableDate", dateObj => {
    return DateTime.fromJSDate(dateObj).toFormat("LLL dd, yyyy");
  });

  // Date formatting (machine readable)
  eleventyConfig.addFilter("machineDate", dateObj => {

    return DateTime.fromJSDate(dateObj).toFormat("yyyy-MM-dd");
  });

  // Minify CSS
  eleventyConfig.addFilter("cssmin", function(code) {
    return new CleanCSS({}).minify(code).styles;
  });

  // Minify JS
  eleventyConfig.addFilter("jsmin", function(code) {
    let minified = UglifyJS.minify(code);
    if (minified.error) {
      console.log("UglifyJS error: ", minified.error);
      return code;
    }
    return minified.code;
  });

  // Minify HTML output
  eleventyConfig.addTransform("htmlmin", function(content, outputPath) {
    if (outputPath.indexOf?.(".html") > -1) {
      let minified = htmlmin.minify(content, {
        useShortDoctype: true,
        removeComments: true,
        collapseWhitespace: true
      });
      return minified;
    }
    return content;
  });

  // Don't process folders with static assets e.g. images
  eleventyConfig.addPassthroughCopy("favicon.ico");
  eleventyConfig.addPassthroughCopy("images/")
  eleventyConfig.addPassthroughCopy("content/images/")
  eleventyConfig.addPassthroughCopy("admin");
  eleventyConfig.addPassthroughCopy("_includes/assets/");
  eleventyConfig.addPassthroughCopy("_includes/experimental/");

  /* Markdown Plugins */
  let markdownIt = require("markdown-it");
  let markdownItAnchor = require("markdown-it-anchor");
  let markdownItEmoji = require("markdown-it-emoji");
  let markdownItFootnote = require("markdown-it-footnote");
  let markdownItContainer = require("markdown-it-container");
  let markdownLinkifyImages = require('markdown-it-linkify-images')
  let markdownToc = require('markdown-it-table-of-contents')
  let markdownItTasks = require('markdown-it-task-lists')
  let markdownItAttrs = require("markdown-it-attrs")
  let markdownItCenterText = require("markdown-it-center-text")
  let options = {
    html: true,
    breaks: false,
    linkify: true,
    typographer: true
  };
  let opts = {
    // permalink: true,
    // permalinkClass: "direct-link",
    // permalinkSymbol: "#"
  };

  eleventyConfig.setLibrary("md", markdownIt(options)
    .use(mdIterator, 'url_new_win', 'link_open', function (tokens, idx) {
      const [attrName, href] = tokens[idx].attrs.find(attr => attr[0] === 'href')
      if (href && (!href.includes('franknoirot.co') && !href.startsWith('/') && !href.startsWith('#'))) {
        tokens[idx].attrPush([ 'target', '_blank' ])
        tokens[idx].attrPush([ 'rel', 'noopener noreferrer' ])
      }
    })
    .use(markdownItAnchor, opts)
    .use(markdownItEmoji)
    .use(markdownItFootnote)
    .use(markdownItContainer, 'callout')
    .use(markdownItContainer, 'callout-blue')
    .use(markdownItContainer, 'callout-pink')
    .use(markdownItContainer, 'callout-green')
    .use(markdownItContainer, 'warning')
    .use(markdownItTasks)
    .use(markdownItCenterText)
    .use(markdownLinkifyImages, {
      imgClass: "p-4",
    })
    .use(markdownItAttrs, {
      includeLevel: [2,3],
      listType: "ol"
    })
  );

  return {
    templateFormats: ["md", "njk", "html", "liquid"],

    // If your site lives in a different subdirectory, change this.
    // Leading or trailing slashes are all normalized away, so don’t worry about it.
    // If you don’t have a subdirectory, use "" or "/" (they do the same thing)
    // This is only used for URLs (it does not affect your file structure)
    pathPrefix: "/",
    markdownTemplateEngine: "liquid",
    htmlTemplateEngine: "njk",
    dataTemplateEngine: "njk",
    dir: {
      input: ".",
      includes: "_includes",
      data: "_data",
      output: "_site"
    }
  };
};

================================================
FILE: .eleventyignore
================================================
README.md
.github/
functions/*
functions/


================================================
FILE: .forestry/settings.yml
================================================
---
new_page_extension: md
auto_deploy: false
admin_path: ''
webhook_url: 
sections:
- type: directory
  path: ''
  label: Test
  create: all
  match: "**/*"
upload_dir: uploads
public_path: "/uploads"
front_matter_path: ''
use_front_matter_path: true
file_template: ":filename:"
build:
  preview_env:
  - ELEVENTY_ENV=dev
  preview_output_directory: _site
  install_dependencies_command: npm install
  preview_docker_image: forestryio/node:12
  mount_path: "/srv"
  working_dir: "/srv"
  instant_preview_command: npx @11ty/eleventy --serve


================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto

# Custom for Visual Studio
*.cs     diff=csharp

# Standard to msysgit
*.doc	 diff=astextplain
*.DOC	 diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot  diff=astextplain
*.DOT  diff=astextplain
*.pdf  diff=astextplain
*.PDF	 diff=astextplain
*.rtf	 diff=astextplain
*.RTF	 diff=astextplain


_site/
node_modules/
package-lock.json
.idea
.vscode
*~


================================================
FILE: .gitignore
================================================
_site/
node_modules/
package-lock.json
.idea
.vscode
*~
.cache
_tmp

================================================
FILE: .nvmrc
================================================
v14


================================================
FILE: 404.md
================================================
---
title: 404
permalink: /404.html
layout: layouts/404.njk
---


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Tim Broeker 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: LICENSE.txt
================================================
Creative Commons
Attribution-NonCommercial-NoDerivs 3.0 Unported

    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. 

License

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

    "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
    "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
    "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
    "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
    "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
    "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
    "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
    "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
    "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.

2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.

3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

    to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
    to Distribute and Publicly Perform the Work including as incorporated in Collections.

The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d).

4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

    You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested.
    You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
    If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.

    For the avoidance of doubt:
        Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
        Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
        Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b).
    Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

    This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
    Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

    Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
    If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
    No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
    This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
    The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.

    Creative Commons Notice

    Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.

    Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.

    Creative Commons may be contacted at https://creativecommons.org/.

================================================
FILE: README.md
================================================
# Prose for Programmers

This is a book for programmers.
It is a guide to mastering the most difficult programming language of all: human language.

Just as with programming,
we write with particular goals in mind.
That goal might be as simple as reminding someone about a requirement
or as complex as justifying an entirely new architecture for a product.
But in every case we write in order to achieve something.

This book will teach you how to achieve those goals more effectively with clearer, more persuasive writing.

## It is a work in progress

The first three chapters are content complete.

* [Why should developers study writing?](manuscript/why.md)
* [General Rules](manuscript/rules.md)
* [The Writing Process](manuscript/process.md)

And there are four chapters to come.

* Writing Structures
* Audience
* Genres
* Other Resources

For more detail, take a look at [the outline](outline.md).

## Can I get it in EPUB/MOBI/PDF?

[Yes, you can.](https://leanpub.com/proseforprogrammers)

## Contributing

* For general questions and suggestions, please [open an issue][issues].
* To correct typos or suggest specific changes in the way something is written, please [open a pull request][prs].

Note: By contributing content to this repository, you grant the book author (Joshua Clanton) a non-exclusive license to use that content in the book as he deems appropriate.

## License

<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/3.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">Prose for Programmers</span> by <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Joshua Clanton</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License</a>.

[issues]:https://github.com/joshuacc/prose-for-programmers/issues
[prs]:https://github.com/joshuacc/prose-for-programmers/pulls

================================================
FILE: _data/site.json
================================================
{
  "name": "Prose for Programmers",
  "subtitle": "Learn to write for people, not just computers",
  "description": "Learn to write for people, not just computers",
  "footer": "<span xmlns:dct='http://purl.org/dc/terms/' href='http://purl.org/dc/dcmitype/Text' property='dct:title' rel='dct:type'><i>Prose for Programmers</i></span> by <span xmlns:cc='http://creativecommons.org/ns#' property='cc:attributionName'>Joshua Clanton</span> is licensed under a <a rel='license' href='http://creativecommons.org/licenses/by-nc-nd/3.0/'>Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License</a>.",
  "url": "https://spacebook.app",
  "githubUrl": "https://github.com/joshuacc/prose-for-programmers",
  "githubBranch": "master",
  "navigationStyle": "vertical",
  "emoji": "✍🏻",
  "enableSearch": false,
  "enableDarkMode": true,
  "enableEditButton": true,
  "enableDatestamp": true,
  "enableGithubLink": true,
  "enableContact": false,
  "enableNetlifyCMS": false,
  "enableComments": false,
  "enableEncryption": false,
  "enablePageNavigation": true
}


================================================
FILE: _includes/assets/css/404.css
================================================
html,
body {
    height: 100%;
    width: 100%;
    margin: 0px;
    background: linear-gradient(90deg, #2f3640 23%, #181b20 100%);
}

.moon {
    background: linear-gradient(90deg, #d0d0d0 48%, #919191 100%);
    position: absolute;
    top: -100px;
    left: -300px;
    width: 900px;
    height: 900px;
    content: '';
    border-radius: 100%;
    box-shadow: 0px 0px 30px -4px rgba(0, 0, 0, 0.5);
}

.moon__crater {
    position: absolute;
    content: '';
    border-radius: 100%;
    background: linear-gradient(90deg, #7a7a7a 38%, #c3c3c3 100%);
    opacity: 0.6;
}

.moon__crater1 {
    top: 250px;
    left: 500px;
    width: 60px;
    height: 180px;
}

.moon__crater2 {
    top: 650px;
    left: 340px;
    width: 40px;
    height: 80px;
    transform: rotate(55deg);
}

.moon__crater3 {
    top: -20px;
    left: 40px;
    width: 65px;
    height: 120px;
    transform: rotate(250deg);
}

.star {
    background: grey;
    position: absolute;
    width: 5px;
    height: 5px;
    content: '';
    border-radius: 100%;
    transform: rotate(250deg);
    opacity: 0.4;
    animation-name: shimmer;
    animation-duration: 1.5s;
    animation-iteration-count: infinite;
    animation-direction: alternate;
}

@keyframes shimmer {
    from {
        opacity: 0;
    }
    to {
        opacity: 0.7;
    }
}

.star1 {
    top: 40%;
    left: 50%;
    animation-delay: 1s;
}

.star2 {
    top: 60%;
    left: 90%;
    animation-delay: 3s;
}

.star3 {
    top: 10%;
    left: 70%;
    animation-delay: 2s;
}

.star4 {
    top: 90%;
    left: 40%;
}

.star5 {
    top: 20%;
    left: 30%;
    animation-delay: 0.5s;
}

.error {
    position: absolute;
    left: 100px;
    top: 400px;
    transform: translateY(-60%);
    font-family: 'Righteous', cursive, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    color: #363e49;
}

.error__title {
    font-size: 10em;
}

.error__subtitle {
    font-size: 2em;
}

.error__description {
    opacity: 0.5;
}

.error__button {
    min-width: 7em;
    margin-top: 3em;
    margin-right: 0.5em;
    padding: 0.5em 2em;
    outline: none;
    border: 2px solid #2f3640;
    background-color: transparent;
    border-radius: 8em;
    color: #576375;
    cursor: pointer;
    transition-duration: 0.2s;
    font-size: 0.75em;
    font-family: 'Righteous', cursive,ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";;
}

.error__button:hover {
    color: #21252c;
}

.error__button--active {
    background-color: #e67e22;
    border: 2px solid #e67e22;
    color: white;
}

.error__button--active:hover {
    box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.5);
    color: white;
}

.astronaut {
    position: absolute;
    width: 185px;
    height: 300px;
    left: 70%;
    top: 50%;
    transform: translate(-50%, -50%) rotate(20deg) scale(1.2);
}

.astronaut__head {
    background-color: white;
    position: absolute;
    top: 60px;
    left: 60px;
    width: 60px;
    height: 60px;
    content: '';
    border-radius: 2em;
}

.astronaut__head-visor-flare1 {
    background-color: #7f8fa6;
    position: absolute;
    top: 28px;
    left: 40px;
    width: 10px;
    height: 10px;
    content: '';
    border-radius: 2em;
    opacity: 0.5;
}

.astronaut__head-visor-flare2 {
    background-color: #718093;
    position: absolute;
    top: 40px;
    left: 38px;
    width: 5px;
    height: 5px;
    content: '';
    border-radius: 2em;
    opacity: 0.3;
}

.astronaut__backpack {
    background-color: #bfbfbf;
    position: absolute;
    top: 90px;
    left: 47px;
    width: 86px;
    height: 90px;
    content: '';
    border-radius: 8px;
}

.astronaut__body {
    background-color: #e6e6e6;
    position: absolute;
    top: 115px;
    left: 55px;
    width: 70px;
    height: 80px;
    content: '';
    border-radius: 8px;
}

.astronaut__body__chest {
    background-color: #d9d9d9;
    position: absolute;
    top: 140px;
    left: 68px;
    width: 45px;
    height: 25px;
    content: '';
    border-radius: 6px;
}

.astronaut__arm-left1 {
    background-color: #e6e6e6;
    position: absolute;
    top: 127px;
    left: 9px;
    width: 65px;
    height: 20px;
    content: '';
    border-radius: 8px;
    transform: rotate(-30deg);
}

.astronaut__arm-left2 {
    background-color: #e6e6e6;
    position: absolute;
    top: 102px;
    left: 7px;
    width: 20px;
    height: 45px;
    content: '';
    border-radius: 8px;
    transform: rotate(-12deg);
    border-top-left-radius: 8em;
    border-top-right-radius: 8em;
}

.astronaut__arm-right1 {
    background-color: #e6e6e6;
    position: absolute;
    top: 113px;
    left: 100px;
    width: 65px;
    height: 20px;
    content: '';
    border-radius: 8px;
    transform: rotate(-10deg);
}

.astronaut__arm-right2 {
    background-color: #e6e6e6;
    position: absolute;
    top: 78px;
    left: 141px;
    width: 20px;
    height: 45px;
    content: '';
    border-radius: 8px;
    transform: rotate(-10deg);
    border-top-left-radius: 8em;
    border-top-right-radius: 8em;
}

.astronaut__arm-thumb-left {
    background-color: #e6e6e6;
    position: absolute;
    top: 110px;
    left: 21px;
    width: 10px;
    height: 6px;
    content: '';
    border-radius: 8em;
    transform: rotate(-35deg);
}

.astronaut__arm-thumb-right {
    background-color: #e6e6e6;
    position: absolute;
    top: 90px;
    left: 133px;
    width: 10px;
    height: 6px;
    content: '';
    border-radius: 8em;
    transform: rotate(20deg);
}

.astronaut__wrist-left {
    background-color: #e67e22;
    position: absolute;
    top: 122px;
    left: 6.5px;
    width: 21px;
    height: 4px;
    content: '';
    border-radius: 8em;
    transform: rotate(-15deg);
}

.astronaut__wrist-right {
    background-color: #e67e22;
    position: absolute;
    top: 98px;
    left: 141px;
    width: 21px;
    height: 4px;
    content: '';
    border-radius: 8em;
    transform: rotate(-10deg);
}

.astronaut__leg-left {
    background-color: #e6e6e6;
    position: absolute;
    top: 188px;
    left: 50px;
    width: 23px;
    height: 75px;
    content: '';
    transform: rotate(10deg);
}

.astronaut__leg-right {
    background-color: #e6e6e6;
    position: absolute;
    top: 188px;
    left: 108px;
    width: 23px;
    height: 75px;
    content: '';
    transform: rotate(-10deg);
}

.astronaut__foot-left {
    background-color: white;
    position: absolute;
    top: 240px;
    left: 43px;
    width: 28px;
    height: 20px;
    content: '';
    transform: rotate(10deg);
    border-radius: 3px;
    border-top-left-radius: 8em;
    border-top-right-radius: 8em;
    border-bottom: 4px solid #e67e22;
}

.astronaut__foot-right {
    background-color: white;
    position: absolute;
    top: 240px;
    left: 111px;
    width: 28px;
    height: 20px;
    content: '';
    transform: rotate(-10deg);
    border-radius: 3px;
    border-top-left-radius: 8em;
    border-top-right-radius: 8em;
    border-bottom: 4px solid #e67e22;
}

================================================
FILE: _includes/assets/css/inline.css
================================================


================================================
FILE: _includes/assets/js/404.js
================================================
(function (window, document) {
    "use strict";
    window.addEventListener('load', function () {
        const cordCanvas = document.getElementById('cord');
        const ctx = cordCanvas.getContext('2d');
        
        let y1 = 160;
        let y2 = 100;
        let y3 = 100;
        
        let y1Forward = true;
        let y2Forward = false;
        let y3Forward = true;
        drawVisor()
        //animate()
    });
  })(window, document);

function drawVisor() {
    const canvas = document.getElementById('visor');
    const ctx = canvas.getContext('2d');
    
    ctx.beginPath();
    ctx.moveTo(5, 45);
    ctx.bezierCurveTo(15, 64, 45, 64, 55, 45);
    
    ctx.lineTo(55, 20);
    ctx.bezierCurveTo(55, 15, 50, 10, 45, 10);
    
    ctx.lineTo(15, 10);
    
    ctx.bezierCurveTo(15, 10, 5, 10, 5, 20);
    ctx.lineTo(5, 45);
    
    ctx.fillStyle = '#2f3640';
    ctx.strokeStyle = '#f5f6fa';
    ctx.fill();
    ctx.stroke();
  }
  

  
  function animate() {
    requestAnimationFrame(animate);
    ctx.clearRect(0, 0, innerWidth, innerHeight);
    
    ctx.beginPath();
    ctx.moveTo(130, 170);
    ctx.bezierCurveTo(250, y1, 345, y2, 400, y3);
    
    ctx.strokeStyle = 'white';
    ctx.lineWidth = 8;
    ctx.stroke();
   
    
    if (y1 === 100) {
      y1Forward = true;
    }
    
    if (y1 === 300) {
      y1Forward = false;
    }
    
    if (y2 === 100) {
      y2Forward = true;
    }
    
    if (y2 === 310) {
      y2Forward = false;
    }
    
    if (y3 === 100) {
      y3Forward = true;
    }
    
    if (y3 === 317) {
      y3Forward = false;
    }
    
    y1Forward ? y1 += 1 : y1 -= 1;
    y2Forward ? y2 += 1 : y2 -= 1;
    y3Forward ? y3 += 1 : y3 -= 1;
  }
  

================================================
FILE: _includes/assets/js/inline.js
================================================
if (window.netlifyIdentity) {
  window.netlifyIdentity.on("init", user => {
    if (!user) {
      window.netlifyIdentity.on("login", () => {
        document.location.href = "/admin/";
      });
    }
  });
}

document.addEventListener("DOMContentLoaded", function() {
  
  const el = document.getElementById("main");
  el.addEventListener("click", closeNavigation, false)

  if (localStorage.getItem('darkmode') === 'true') {
    document.body.classList.add("dark");
  } else {
    document.body.classList.remove("dark");
  }
});

function logout() {
  localStorage.removeItem("passphrase")
  window.location.href = "/";
}

function showNavigation() {
  const navigation = document.getElementById("navigation");
  navigation.classList.remove("hidden", "sticky","pt-32"); 
  navigation.classList.add("absolute","right-0", "top-0", "-mt-0", "z-50", "pt-0", "bg-white","border-l", "border-gray-200"); 
}

function closeNavigation() {
  const navigation = document.getElementById("navigation");
  navigation.classList.add("hidden"); 
  navigation.classList.remove("absolute","right-0","z-50", "bg-gray-100", "border-r", "border-gray-800" ); 
}

function activateDarkMode() {
  if (localStorage.getItem('darkmode') === 'true') {
    localStorage.setItem('darkmode', 'false')
    document.body.classList.remove("dark");
  } else {
    localStorage.setItem('darkmode', 'true')
    document.body.classList.add("dark");
  }
}

function toggleLayout(state) {
  if (localStorage.getItem('layout') === "horizontal") {
    localStorage.setItem('layout', 'vertical')
  } else if (localStorage.getItem('layout') === "vertical") {
    localStorage.setItem('layout', "horizontal")
  } else if (!localStorage.getItem('layout')) {
    if (state === "horizontal") {
      localStorage.setItem('layout', 'vertical')
    } else {
      localStorage.setItem('layout', 'horizontal')
    }
  } 
  
 
  console.log(localStorage.getItem('layout'))
}

================================================
FILE: _includes/assets/js/search.js
================================================
(function (window, document) {
    "use strict";
    const search = (e) => {
      const results = window.searchIndex.search(e.target.value, {
        bool: "OR",
        expand: true,
      });
      const searchBox = document.getElementById("searchField");
      const resEl = document.getElementById("searchResults");
      const noResultsEl = document.getElementById("noResultsFound");
      const navigation = document.getElementById("navigation");

      searchBox.addEventListener('focus', (event) => {
        event.target.classList.remove("hidden"); 
        resEl.classList.remove("hidden"); 
      });

    document.addEventListener('click', function(event) {
        var isClickInside = searchBox.contains(event.target);
        if (!isClickInside) {
            console.log('hidden')
            resEl.classList.add("hidden"),noResultsEl.classList.add("hidden")
            noResultsEl.classList.add("hidden")
        }
      });

    resEl.innerHTML = "";
      if (e.target.value != "") {
        if (results != "") {
          noResultsEl.classList.add("hidden")
          resEl.classList.add("p-4")
          results.map((r) => {
            const { id, title, description } = r.doc;
            const el = document.createElement("li", { tabindex: '-1' });
            resEl.appendChild(el);
    
            const h3 = document.createElement("h3");
            el.appendChild(h3);
    
            const a = document.createElement("a");
            a.setAttribute("href", id);
            a.textContent = title;
            h3.appendChild(a);
    
            const p = document.createElement("p");
            p.textContent = description;
            el.appendChild(p);
          });
        } else {
          noResultsEl.classList.remove("hidden")
        }
      } else {
        noResultsEl.classList.add("hidden")
      }
    };
    fetch("/search-index.json").then((response) =>
      response.json().then((rawIndex) => {
        window.searchIndex = elasticlunr.Index.load(rawIndex);
        document.getElementById("searchField").addEventListener("input", search);
      })
    );
  })(window, document);

================================================
FILE: _includes/assets/utils/image.txt
================================================
{% image "sagan.jpg" "Carl Sagan, 1987" %}

================================================
FILE: _includes/assets/utils/imagesize.txt
================================================
{% image "sagan.jpg" "Carl Sagan, 1987" "200px" %}

================================================
FILE: _includes/components/contact.njk
================================================
<div class="flex w-full">
  <div class="w-full bg-white dark:bg-gray-700 rounded shadow-lg p-8 md:max-w-lg md:mx-auto">
    <form name="contact" class="mb-4 md:flex md:flex-wrap md:justify-between" action="/" method="post"  netlify>
    <h1>Send a message</h1>
      <div class="flex flex-col mb-4 md:w-1/2">
        <label class="mb-2 uppercase tracking-wide font-bold text-lg text-grey-darkest dark:text-gray-400" for="first_name">First Name</label>
        <input class="border py-2 px-3 text-grey-darkest md:mr-2" type="text" name="first_name" id="first_name">
      </div>
      <div class="flex flex-col mb-4 md:w-1/2">
        <label class="mb-2 uppercase font-bold text-lg text-grey-darkest dark:text-gray-400 md:ml-2" for="last_name">Last Name</label>
        <input class="border py-2 px-3 text-grey-darkest md:ml-2" type="text" name="last_name" id="last_name">
      </div>
      <div class="flex flex-col mb-4 md:w-full">
        <label class="mb-2 uppercase font-bold text-lg text-grey-darkest dark:text-gray-400" for="email">Email</label>
        <input class="border py-2 px-3 text-grey-darkest" type="email" name="email" id="email">
      </div>
      <div class="flex flex-col mb-6 md:w-full">
        <label class="mb-2 uppercase font-bold text-lg text-grey-darkest dark:text-gray-400" for="message">Message</label>
        <textarea class="border py-2 px-3 text-grey-darkest" name="message" id="message" placeholder="Write your message here" rows="7" required></textarea>
      </div>
      <button class="block bg-green-600 hover:bg-blue-600 text-white uppercase text-lg mx-auto p-4 rounded" type="submit">Send message</button>
    </form>
  </div>
</div>


================================================
FILE: _includes/components/footer.njk
================================================
<div class="z-50 mt-12 h-12 flex dark:bg-gray-900 text-gray-500 dark:text-gray-400 justify-center p-2 border-t border-gray-100 dark:border-gray-800">
 <div class="flex w-full items-center ">
 
  <div class="w-6 h-6 flex-none">
  {% if site.enableContact == 1 %}
  <a aria-label="Contact" href="/contact" rel="noopener noreferrer"> 
  <svg width="24" height="24"  class="text-gray-500 dark:text-gray-400" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M1 3.5l.5-.5h13l.5.5v9l-.5.5h-13l-.5-.5v-9zm1 1.035V12h12V4.536L8.31 8.9H7.7L2 4.535zM13.03 4H2.97L8 7.869 13.03 4z"/></svg>
  </a>
  {% endif %}
  </div>
   
  <div class="text-gray-500 flex flex-grow justify-center text-align-center">
     <small>
    {% if site.footer %}
    {{ site.footer | safe }}
    {% endif %}
    </small>
  </div>
  {% if site.enableNetlifyCMS == 1 %}
  <div class="flex-none float-right">
    <a aria-label="Netlify CMS" href="{{metadata.url}}/admin/" rel="noopener noreferrer" target="_blank"> 
    <svg width="24px" viewBox="0 0 16 16" class="bi bi-gear" class="text-gray-300 dark:text-gray-500" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
      <path class="text-gray-300 dark:text-gray-500" fill-rule="evenodd" d="M8.837 1.626c-.246-.835-1.428-.835-1.674 0l-.094.319A1.873 1.873 0 0 1 4.377 3.06l-.292-.16c-.764-.415-1.6.42-1.184 1.185l.159.292a1.873 1.873 0 0 1-1.115 2.692l-.319.094c-.835.246-.835 1.428 0 1.674l.319.094a1.873 1.873 0 0 1 1.115 2.693l-.16.291c-.415.764.42 1.6 1.185 1.184l.292-.159a1.873 1.873 0 0 1 2.692 1.116l.094.318c.246.835 1.428.835 1.674 0l.094-.319a1.873 1.873 0 0 1 2.693-1.115l.291.16c.764.415 1.6-.42 1.184-1.185l-.159-.291a1.873 1.873 0 0 1 1.116-2.693l.318-.094c.835-.246.835-1.428 0-1.674l-.319-.094a1.873 1.873 0 0 1-1.115-2.692l.16-.292c.415-.764-.42-1.6-1.185-1.184l-.291.159A1.873 1.873 0 0 1 8.93 1.945l-.094-.319zm-2.633-.283c.527-1.79 3.065-1.79 3.592 0l.094.319a.873.873 0 0 0 1.255.52l.292-.16c1.64-.892 3.434.901 2.54 2.541l-.159.292a.873.873 0 0 0 .52 1.255l.319.094c1.79.527 1.79 3.065 0 3.592l-.319.094a.873.873 0 0 0-.52 1.255l.16.292c.893 1.64-.902 3.434-2.541 2.54l-.292-.159a.873.873 0 0 0-1.255.52l-.094.319c-.527 1.79-3.065 1.79-3.592 0l-.094-.319a.873.873 0 0 0-1.255-.52l-.292.16c-1.64.893-3.433-.902-2.54-2.541l.159-.292a.873.873 0 0 0-.52-1.255l-.319-.094c-1.79-.527-1.79-3.065 0-3.592l.319-.094a.873.873 0 0 0 .52-1.255l-.16-.292c-.892-1.64.902-3.433 2.541-2.54l.292.159a.873.873 0 0 0 1.255-.52l.094-.319z"/>
      <path class="text-gray-300 dark:text-gray-500" fill-rule="evenodd" d="M8 5.754a2.246 2.246 0 1 0 0 4.492 2.246 2.246 0 0 0 0-4.492zM4.754 8a3.246 3.246 0 1 1 6.492 0 3.246 3.246 0 0 1-6.492 0z"/>
    </svg> 
    </a>
  </div>
  {% endif %}
  {% if site.enableGithubLink == 1 %}
  <div class="flex-none ml-2 float-right">
    <a aria-label="github link" rel="noopener noreferrer" href="{{ site.githubUrl}}" target="_blank"><svg height="28" viewBox="0 0 24 24" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" class="text-gray-400 dark:text-gray-400" d="M12 3C7.0275 3 3 7.12937 3 12.2276C3 16.3109 5.57625 19.7597 9.15374 20.9824C9.60374 21.0631 9.77249 20.7863 9.77249 20.5441C9.77249 20.3249 9.76125 19.5982 9.76125 18.8254C7.5 19.2522 6.915 18.2602 6.735 17.7412C6.63375 17.4759 6.19499 16.6569 5.8125 16.4378C5.4975 16.2647 5.0475 15.838 5.80124 15.8264C6.51 15.8149 7.01625 16.4954 7.18499 16.7723C7.99499 18.1679 9.28875 17.7758 9.80625 17.5335C9.885 16.9337 10.1212 16.53 10.38 16.2993C8.3775 16.0687 6.285 15.2728 6.285 11.7432C6.285 10.7397 6.63375 9.9092 7.20749 9.26326C7.1175 9.03257 6.8025 8.08674 7.2975 6.81794C7.2975 6.81794 8.05125 6.57571 9.77249 7.76377C10.4925 7.55615 11.2575 7.45234 12.0225 7.45234C12.7875 7.45234 13.5525 7.55615 14.2725 7.76377C15.9937 6.56418 16.7475 6.81794 16.7475 6.81794C17.2424 8.08674 16.9275 9.03257 16.8375 9.26326C17.4113 9.9092 17.76 10.7281 17.76 11.7432C17.76 15.2843 15.6563 16.0687 13.6537 16.2993C13.98 16.5877 14.2613 17.1414 14.2613 18.0065C14.2613 19.2407 14.25 20.2326 14.25 20.5441C14.25 20.7863 14.4188 21.0746 14.8688 20.9824C16.6554 20.364 18.2079 19.1866 19.3078 17.6162C20.4077 16.0457 20.9995 14.1611 21 12.2276C21 7.12937 16.9725 3 12 3Z" fill="currentColor"></path></svg></a>
  </div>
  {% endif %}
</div> 
</div>


================================================
FILE: _includes/components/head.njk
================================================
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ renderData.title or title }} - {{ site.name }}</title>
  <meta name="description" content="{{ metaDescription or renderData.metaDescription or site.description }}">
  <link rel="stylesheet" href="/style.css?v={% version %}"/>
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>{{ site.emoji }}</text></svg>">
  
  {% set js %}
    {% include "assets/js/inline.js" %}
    {% include "assets/js/search.js" %}
  {% endset %}
  
  <script defer>{{ js | jsmin | safe }}</script>

 {% if site.enableNetlifyCMS == 1 %}
  <link rel="dns-prefetch" href="https://identity.netlify.com">
  <script async src="https://identity.netlify.com/v1/netlify-identity-widget.js" defer></script>
 {% endif %}

  {% if site.enableSearch == 1 %}
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/elasticlunr/0.9.6/elasticlunr.min.js" defer></script>
  {% endif %}

  {% if site.navigationStyle == 'horizontal' %}
  <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.3/dist/alpine.min.js" defer></script>
  {% endif %}

</head>


================================================
FILE: _includes/components/header.njk
================================================
<div class="fixed w-full top-0 left-0 z-10 "> 
    <nav class="flex h-16 md:h-20 items-center bg-white dark:bg-gray-900 z-20 fixed top-0 left-0 right-0 border-b border-gray-100 px-0 py-2 dark:bg-dark dark:border-gray-800 transition-colors duration-200">
      <div class="hidden md:block space-y-4 w-full p-4">
        <a class="no-underline text-gray-600 hover:text-gray-800 font-bold dark:text-gray-500 items-center" href="/">
          <span class="text-xl">{{ site.name }}</span>
          {% if site.subtitle %}
          <span class="block mt-1 text-sm text-gray-500 hover:text-gray-600 dark:text-gray-600 font-normal text-xs sm:text-xs md:text-sm lg:text-sm w-full"> {{ site.subtitle }}</span>
          {% endif %}
         </a>
      </div>
      <div class="mr-2 relative w-full md:w-64">
       {% if site.enableSearch == 1 %}
        <input  aria-label="Search" x-data="{ isOpen: true }" x-bind:class="{ 'hidden': !isOpen }" type="search" placeholder="Search..." class="ml-2 w-60 bg-gray-50 dark:bg-gray-800 appearance-none border dark:border-gray-700 rounded py-2 px-2 placeholder-gray-300 leading-tight focus:ring-4 focus:ring-blue-200 dark:focus:ring-gray-700 ring-opacity-50 border-gray-200" id="searchField">
        <ul tabindex="0" class="bg-white dark:bg-gray-200 z-50 shadow-md p-0 w-full list-none m-0 absolute left-0 md:right-0 bg-white rounded mt-1 top-100 z-50" id="searchResults"></ul>
        <div class="hidden shadow-md list-none p-4 m-0 absolute left-0 md:right-0 bg-white rounded mt-1 border top-100 divide-y divide-gray-300 z-2" id="noResultsFound">
          <p>No results found.</p>
        </div>
        {% endif %}
      </div>

      <button  aria-label="Show navigation" onclick="showNavigation()" class="block md:hidden mr-3"><svg fill="none" width="24" height="24" viewBox="0 0 24 24" class="text-gray-400 hover:text-gray-600" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg></button>
       
      {% if site.enableDarkMode == 1 %}
      <a aria-label="dark mode" rel="noopener noreferrer" class="mr-3 switch text-current cursor-pointer" onclick="activateDarkMode()"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16" height="24" xmlns="http://www.w3.org/2000/svg"><path class="text-gray-400 hover:text-yellow-400 dark:text-yellow-400" fill-rule="evenodd" clip-rule="evenodd" d="M11.67 8.658a3.661 3.661 0 0 0-.781 1.114 3.28 3.28 0 0 0-.268 1.329v1.6a1.304 1.304 0 0 1-.794 1.197 1.282 1.282 0 0 1-.509.102H7.712a1.285 1.285 0 0 1-.922-.379 1.303 1.303 0 0 1-.38-.92v-1.6c0-.479-.092-.921-.274-1.329a3.556 3.556 0 0 0-.776-1.114 4.689 4.689 0 0 1-1.006-1.437A4.187 4.187 0 0 1 4 5.5a4.432 4.432 0 0 1 .616-2.27c.197-.336.432-.64.705-.914a4.6 4.6 0 0 1 .911-.702c.338-.196.7-.348 1.084-.454a4.45 4.45 0 0 1 1.2-.16 4.476 4.476 0 0 1 2.276.614 4.475 4.475 0 0 1 1.622 1.616 4.438 4.438 0 0 1 .616 2.27c0 .617-.117 1.191-.353 1.721a4.69 4.69 0 0 1-1.006 1.437zM9.623 10.5H7.409v2.201c0 .081.028.15.09.212a.29.29 0 0 0 .213.09h1.606a.289.289 0 0 0 .213-.09.286.286 0 0 0 .09-.212V10.5z"></path></svg></a>
      {% endif %}

      {% if site.enabledEncryption == 1 %} 
        <button  aria-label="Logout" onclick="logout()" class="block  mr-3">
        <svg  width="24" height="24"  xmlns='http://www.w3.org/2000/svg' class='ionicon text-gray-400 hover:text-red-800' viewBox='0 0 512 512'><title>Close Circle</title><path d='M448 256c0-106-86-192-192-192S64 150 64 256s86 192 192 192 192-86 192-192z' fill='none' stroke='currentColor' stroke-miterlimit='10' stroke-width='32'/><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='32' d='M320 320L192 192M192 320l128-128'/></svg>
        </button>
      {% endif %}
    </nav>  
    {% if site.navigationStyle == 'horizontal' %}
      <div class="mt-20">
      {% include "components/nav.njk" %}
      </div>
    {% endif %}
 </div>


  

================================================
FILE: _includes/components/nav.njk
================================================
{% set navPages = collections.all | eleventyNavigation  %}

{% macro renderNavListItem(entry,rel) -%}

  {% if entry.url == page.url or entry.title == eleventyNavigation.parent or entry.title == eleventyComputed.key%}
    {% if rel == "child" %}
      {% set highlight = 'border-gray-600 font-semibold text-gray-500 focus:border-gray-700' %}
    {% else %}
      {% set highlight = 'border-gray-600 font-semibold focus:border-gray-700' %}
    {% endif %}
    {% set current = 'aria-current="page"' %}
  {% else %}
    {% set highlight = 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-500 ' %}
    {% set current = '' %}
  {% endif %}
 
{%- if not entry.children.length  -%}
  {% if rel == "child" %}
   <a class="w-max 
    rounded  
    px-4 py-2 
    block
    focus:text-gray-900 
    text-sm bg-transparent 
    dark:bg-transparent
    dark:hover:text-gray-400 
    md:mt-0 
    m-0
    hover:font-bold
    focus:text-gray-900   
    focus:outline-none focus:ring {{ highlight }} 
    text-gray-500 
    dark:text-gray-500" 
    href="{{ entry.url | url }}">{{ entry.title }}</a>
  {% else %}
   <a class="w-max
    px-4 py-3 
    rounded 
    block
    text-sm 
    bg-transparent 
    rounded-lg 
    dark:bg-transparent 
    dark:focus:bg-gray-600 
    dark:focus:text-white 
    dark:hover:text-gray-500 
    dark:text-gray-400 
    md:mt-0 
    hover:text-gray-900 
    focus:text-gray-900 
    focus:outline-none 
    focus:ring {{ highlight }} 
    text-gray-500" 
    href="{{ entry.url | url }}">{{ entry.title }}</a>
  {% endif %}
 
  {%- else -%}
   
    {%- if entry.children.length -%}
    
    <div @click.away="open = false" class="relative" x-data="{ open: false }">
        <button  @click="open = !open" 
        class="whitespace-nowrap flex flex-row items-center w-full lg:mr-2 px-4 py-2 mt-2 text-sm text-left bg-transparent rounded-lg dark:bg-transparent dark:focus:text-white dark:hover:text-gray-400 md:w-auto md:inline md:mt-0 md:ml-4 hover:text-gray-900 hover:font-semibold focus:text-gray-900   focus:outline-none focus:ring {{ highlight }}">
          <span>{{ entry.title }}</span>
          <svg fill="currentColor" viewBox="0 0 20 20" :class="{'rotate-180': open, 'rotate-0': !open}" class="inline w-4 h-4 mt-1 ml-1 transition-transform duration-200 transform md:-mt-1"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
        </button>
        <div x-show="open" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95"  x-transition="bg-gray-100 bg-white" class="shadow bg-white absolute rounded-lg top-8 w-max ml-4 mt-2">
          <div class="py-2 w-max pr-12 dark:bg-gray-800">
          {%- for child in entry.children %}{{ renderNavListItem(child) }}{% endfor -%}
          </div>
        </div>
      </div>
    {%- endif -%}
  {%- endif -%}
{%- endmacro %}

<div class="md:bg-white dark:bg-gray-900 p-0 mt-8 pb-8 mr-1  flex w-full justify-end">
  <div x-data="{ open: false }" class="flex flex-col justify-start md:items-center  md:flex-row ">
   
    <nav x-ref="dropdown"  :class=" {'flex': open, 'hidden': !open}" class="hidden md:block mt-4 mr-2 justify-end flex-col flex-grow pb-4 md:pb-0 hidden md:flex md:flex-row">
    {%- for entry in navPages %}
    {{ renderNavListItem(entry,'child') }}
    {%- endfor -%}
    </nav>

    <nav id="navigation" class="md:hidden h-screen fixed inset-y-0 overflow-x-hidden overflow-y-auto  hidden w:64 lg:w-72 mt-20 md:block  dark:bg-gray-900  flex-none top-0  p-4 w-64 md:border-r border-b border-gray-100 dark:border-gray-800">
        <button  name="Close navigation" onclick="closeNavigation()" class="float-right justify-end block md:hidden -mr-4 p-6">	<svg xmlns="http://www.w3.org/2000/svg" class="dark:bg-gray-500" width="18" height="18" viewBox="0 0 18 18"><path class="text-gray-100" d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/></svg></button>
        <div class=" clear-right">
          <a href="/">
          <div class="block md:hidden pt-4 font-semibold text-gray-500">{{ site.name }}</div>
          <div class="block md:hidden text-sm text-gray-500 mb-4">{{ site.subtitle }}</div>
          </a>
          {{ collections.all | eleventyNavigation | eleventyNavigationToHtml({anchorClass: "item",activeAnchorClass: "active",activeListItemClass: "active",activeKey: eleventyNavigation.key, listClass: "nav", listItemClass: "item"}) | safe }}
        </div>
      </nav>
  </div>
</div>




================================================
FILE: _includes/experimental/blog/author.njk
================================================
---
title: Author archive
layout: layouts/base.njk
pagination:
  data: collections.authors
  size: 1
  alias: author
permalink: "authors/{{ author | slug }}/"
renderData:
  author: "{{ author }}"
  title: "Posts by {{ author }}"
  metaDescription: "An archive of all posts by the author: {{ author }}."
---

<h1>{{ renderData.title | safe }}</h1>

<section>
	{% for post in collections.authors[author] | reverse %}
		<article{% if post.page.url == url %} data-current="current item"{% endif %}>
			<h3>
				<a href="{{ post.page.url | url }}">
					{% if post.title %}
						{{ post.title }}
					{% else %}
						Untitled
					{% endif %}
				</a>
			</h3>
			{% if post.summary %}
				<p>
					{{ post.summary }}
				</p>
			{% endif %}
			<p>
				<time datetime="{{ post.date | machineDate }}">
					<small>{{ post.date | readableDate }} by <a href="/authors/{{ post.author | slug }}/">{{ post.author }}</a></small>
				</time>
			</p>
			{% if post.tags %}
				<p>
					{% for tag in post.tags %}
		  			{%- if tag != "post" -%}
		  				{% set tagUrl %}/tags/{{ tag }}/{% endset %}
		  			<a href="{{ tagUrl | url }}" rel="tag">{{ tag }}</a>
		  			{%- endif -%}
					{% endfor %}
				</p>
			{% endif %}
		</article>
	{% endfor %}
  <nav>
    <a href="{{ '/authors/' | url }}">← Authors index</a>
  </nav>
</section>

================================================
FILE: _includes/experimental/blog/authors.njk
================================================
---
title: Authors
metaDescription: This page lists all of the Blog's authors.
layout: layouts/base.njk
pagination:
  data: collections.authors
  size: Infinity
permalink: "authors/index.html"
---

<h1>{{ title }}</h1>

<section>
  {% for author in pagination.items %}
    <article>
      <h3>
        <a href="/authors/{{ author | slug }}">{{ author | safe }}</a>
      </h3>
    </article>
  {% endfor %}
  <nav>
    <a href="{{ '/blog/' | url }}">← Blog index</a>
  </nav>
</section>

================================================
FILE: _includes/experimental/blog/components/pageslist.njk
================================================
<section>
	{% for page in pageslist | reverse %}
		<article{% if post.url == url %} data-current="current item"{% endif %}>
			<h3>
				<a href="{{ page.url | url }}">
					{% if page.data.title %}
						{{ page.data.title }}
					{% else %}
						Untitled
					{% endif %}
				</a>
			</h3>
			{% if page.data.summary %}
				<p>
					{{ page.data.summary }}
				</p>
			{% endif %}
			<p>
				<time datetime="{{ page.date | machineDate }}">
					{# <small>{{ post.date | readableDate }} by <a href="/authors/{{ post.data.author | slug }}/">{{ post.data.author }}</a></small> #}
				</time>
			</p>
			{% if page.data.tags %}
				<p>
					{% for tag in page.data.tags %}
		  			{%- if tag != "post" -%}
		  				{% set tagUrl %}/tags/{{ tag }}/{% endset %}
		  			<a href="{{ tagUrl | url }}" rel="tag">{{ tag }}</a>
		  			{%- endif -%}
					{% endfor %}
				</p>
			{% endif %}
		</article>
	{% endfor %}
</section>


================================================
FILE: _includes/experimental/blog/components/postslist.njk
================================================
<section>
<ul class="spacelog">
	{% for post in postslist | reverse %}
	<li>
		<time datetime="{{ post.date | machineDate }}">
		<small>{{ post.date | readableDate }} — </small>
		</time>  <a href="{{ post.url | url }}">
		{% if post.data.title %}
			{{ post.data.title }}
		{% else %}
			Untitled
		{% endif %}
			</a>
			{% if post.data.tags %}
					{% for tag in post.data.tags %}
		  			{%- if tag != "post" -%}
		  				{% set tagUrl %}/tags/{{ tag }}/{% endset %}
		  			<a class="text-xs ml-1 px-2 py-1 text-gray-600 bg-gray-200 no-underline rounded" href="{{ tagUrl | url }}" rel="tag">{{ tag }}</a>
		  			{%- endif -%}
					{% endfor %}
			{% endif %}
			</li>
	{% endfor %}
	</ul>
		{% if post.data.tags %}
				<p>
					{% for tag in post.data.tags %}
		  			{%- if tag != "post" -%}
		  				{% set tagUrl %}/tags/{{ tag }}/{% endset %}
		  			<a href="{{ tagUrl | url }}" rel="tag">{{ tag }}</a>
		  			{%- endif -%}
					{% endfor %}
				</p>
			{% endif %}
		</article>
</section>
{# 
<h1>Foo</h1>
{% set latest_posts = collections.posts %}
{% for post in latest_posts.slice(0,2) | reverse %}
{{ post.data.title}}
{% endfor %} #}


================================================
FILE: _includes/experimental/blog/content/posts/firstpost.md
================================================
---
title: This is the first example post
metaDescription: This is a sample meta description. If one is not present in your page/post's front matter, the default metadata.desciption will be used instead.
date: 2019-01-01T00:00:00.000Z
author: John Appleseed
summary: Why contemplating our mortality can be a powerful catalyst for change
tags:
  - tech
  - environment
  - politics
  - sport
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.

Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.

## Section Header

Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.

``` text/2-3
// this is a command
function myCommand() {
	let counter = 0;
	counter++;
}
```
Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.


================================================
FILE: _includes/experimental/blog/content/posts/fourthpost.md
================================================
---
title: This is the fourth example post
date: 2020-02-03
author: John Doe
summary: Why contemplating our mortality can be a powerful catalyst for change
tags:
  - environment
  - politics
---
Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.

![A sample inlined image](https://source.unsplash.com/random/600x400)

Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.


================================================
FILE: _includes/experimental/blog/content/posts/posts.json
================================================
{
  "layout": "layouts/post.njk",
  "permalink": "posts/{{ title | slug }}/index.html",
  "author": "Anonymous",
  "tags": [
    "post"
  ]
}


================================================
FILE: _includes/experimental/blog/content/posts/secondpost.md
================================================
---
title: This is the second example post
summary: Why contemplating our mortality can be a powerful catalyst for change
date: 2020-01-01
author: John Appleseed
tags:
  - sport
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.

## Section Header

Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.

Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.


================================================
FILE: _includes/experimental/blog/content/posts/the-fifth-and-hopefully-final-example-post.md
================================================
---
title: The fifth and hopefully final example post yo yo
date: 2020-10-15T12:23:39.598Z
author: Jane Doe
summary: Why contemplating our mortality can be a powerful catalyst for change
tags:
  - environment
  - sport
eleventyComputed:
  key: Spacelog 
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.

![A sample inlined image](https://source.unsplash.com/random/600x400)

Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.

================================================
FILE: _includes/experimental/blog/content/posts/thirdpost.md
================================================
---
title: This is the third example post which has a slightly longer title than the others
date: 2020-01-01
author: Jane Doe
summary: Why contemplating our mortality can be a powerful catalyst for change
tags:
  - tech
  - politics
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.

```
pre,
code {
	line-height: 1.5;
}
```

Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.

## Section Header

Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.


================================================
FILE: _includes/experimental/blog/layouts/blog.njk
================================================
---
layout: layouts/base.njk
section: blog
permalink: /blog/index.html
---
<div class="flex w-full justify-center">
    <div class="mt-4 px-6 md:px-6 lg:px-8 xl:px-12 w-full max-w-5xl">
        <div class="wrapper flex justify-between">
             <div class="main  flex flex-col pr-0 prose sm:prose lg:prose-lg xl:prose-md">
                <article>

<h1>{{ title }}</h1>

{{ layoutContent | safe }}

{% set postslist = collections.post %}
{% include "components/postslist.njk" %}
 </article>
               
            </div>    
            
         
        </div>
    </div>
</div>
   



================================================
FILE: _includes/experimental/blog/layouts/post.njk
================================================
---
layout: layouts/base.njk
section: post
---
{% if site.navigationStyle == "vertical" %}
    {% if site.enableEditButton == true or site.enableDatestamp == true %}
    <div class="flex mt-4 justify-end mr-1">
        {% if site.enableDatestamp == true %}
        <div class="inline-block w-auto text-xs text-gray-500 pt-1 pb-1 pl-3 mr-2 rounded ">Updated <time datetime="{{ date | machineDate }}">{{ date | readableDate }}</time></div>
        {% endif %}
        {% if site.enableEditButton == true %}
        <div class="inline-block w-auto text-xs  text-gray-500 hover:bg-gray-200 pt-1 pr-4 pb-1 pl-3 rounded "><a href="{{ site.githubUrl }}/edit/{{ site.githubBranch }}/{{ page.inputPath }}">Edit</a></div>
        {% endif %}
    </div>
    {% endif %}
{% endif %}
 
<div class="flex w-full justify-center">
    <div class="mt-4 px-6 md:px-6 lg:px-8 xl:px-12 w-full max-w-5xl">
        <div class="wrapper flex justify-between">
            {% if site.enableTOC and toc %} 
            <div class="main flex flex-col pr-0 xl:pr-64 prose sm:prose lg:prose-lg xl:prose-md">
            {% else %}
             <div class="main  flex flex-col pr-0 prose sm:prose lg:prose-lg xl:prose-md">
            {% endif %}    
                <article>
                <h1 class="dark:text-gray-500">{{ title }}</h1>
                {% if site.navigationStyle == "horizontal" %}
                    {% if site.enableEditButton == true or site.enableDatestamp == true %}
                        <div class="flex mt-4 justify-end mr-1">
                        {% if site.enableDatestamp == true %}
                            <div class="inline-block w-auto text-xs text-gray-500 pt-1 pb-1 pl-3 mr-2 rounded ">Updated <time datetime="{{ date | machineDate }}">{{ date | readableDate }}</time></div>
                        {% endif %}
                        {% if site.enableEditButton == true %}
                            <div class="inline-block w-auto text-xs  text-gray-500 hover:bg-gray-200 pt-1 pr-4 pb-1 pl-3 rounded "><a class="text-gray-500 no-underline font-normal !important" href="{{ site.githubUrl }}/edit/{{ site.githubBranch }}/{{ page.inputPath }}">Edit</a></div>
                        {% endif %}
                        </div>
                    {% endif %}
                {% endif %}
                <div class="adjust dark:text-gray-400">
                    {{ layoutContent | safe }}
                    {% if (site.enableComments) and (comments !== 0) %}
                        <!-- Paste your comment code here! -->
                    {% endif %}
                    
                   {% if tags %}
    <p>
    {% for tag in tags %}
      {%- if tag != "post" -%}
        {% set tagUrl %}/tags/{{ tag }}/{% endset %}
        <a class="text-xs px-3 py-2 text-gray-600 bg-gray-200 no-underline rounded" href="{{ tagUrl | url }}" rel="tag">{{ tag }}</a>
      {%- endif -%}
    {% endfor %}
    </p>
  {% endif %}

<hr />
  {% if nextPost.url %}
    <p class="ctr">
      <strong>Next</strong>: 
      <a class="next" href="{{ nextPost.url }}">{{ nextPost.data.title }}</a>
    </p>
  {% endif %}
  {% if prevPost.url %}
    <p class="ctr">
      <strong>Previous</strong>: 
      <a class="previous" href="{{ prevPost.url }}">{{ prevPost.data.title }}</a>
    </p>
  {% endif %}
<nav>
  <a href="{{ '/blog/' | url }}">← Spacelog</a>
</nav>

                </div>
                </article>
            </div>    
            
        </div>
    </div>
</div>
 

================================================
FILE: _includes/experimental/blog/pages/blog.md
================================================
---
layout: layouts/blog.njk
title: Spacelog 
date: 2020-12-21
permalink: /blog/index.html
eleventyNavigation:
  key: Spacelog
  order: 200
---

================================================
FILE: _includes/experimental/blog/tags.njk
================================================
---
pagination:
  data: collections
  size: 1
  alias: tag
  filter:
    - all
    - nav
    - post
    - posts
    - page
    - pages
permalink: /tags/{{ tag }}/
layout: layouts/base.njk
renderData:
  title: {{ tag }}
  metaDescription: "All content tagged with “{{ tag }}”"
---
<div class="flex w-full justify-center">
    <div class="mt-4 px-6 md:px-6 lg:px-8 xl:px-12 w-full max-w-5xl">
        <div class="wrapper flex justify-between">
             <div class="main  flex flex-col pr-0 prose sm:prose lg:prose-lg xl:prose-md">
<h1>Tagged: {{ tag }}</h1>

{% set postslist = collections[tag] %}
{% include "components/postslist.njk" %}

<nav>
  <a href="{{ '/blog/' | url }}">← Blog index</a>
</nav>

 </div>    
            
         
        </div>
    </div>
</div>
   



================================================
FILE: _includes/layouts/404.njk
================================================
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
       {% set css %}
    {% include "_includes/assets/css/404.css" %}
  {% endset %}
  <style>{{ css | cssmin | safe }}</style>
     {% set js %}
    {% include "_includes/assets/js/404.js" %}
  {% endset %}
  <script>{{ js | jsmin | safe }}</script> 
<div class="moon"></div>
<div class="moon__crater moon__crater1"></div>
<div class="moon__crater moon__crater2"></div>
<div class="moon__crater moon__crater3"></div>

<div class="star star1"></div>
<div class="star star2"></div>
<div class="star star3"></div>
<div class="star star4"></div>
<div class="star star5"></div>

<div class="error">
  <div class="error__title">404</div>
  <div class="error__subtitle">Ground Control to Major Tom</div>
  <div class="error__description">Your circuit's dead, there's something wrong</div>
  <a href="/"><button class="error__button error__button--active">Go home</button></a>
</div>

<div class="astronaut">
  <div class="astronaut__backpack"></div>
  <div class="astronaut__body"></div>
  <div class="astronaut__body__chest"></div>
  <div class="astronaut__arm-left1"></div>
  <div class="astronaut__arm-left2"></div>
  <div class="astronaut__arm-right1"></div>
  <div class="astronaut__arm-right2"></div>
  <div class="astronaut__arm-thumb-left"></div>
  <div class="astronaut__arm-thumb-right"></div>
  <div class="astronaut__leg-left"></div>
  <div class="astronaut__leg-right"></div>
  <div class="astronaut__foot-left"></div>
  <div class="astronaut__foot-right"></div>
  <div class="astronaut__wrist-left"></div>
  <div class="astronaut__wrist-right"></div>
  
  <div class="astronaut__cord">
    <canvas id="cord" height="500px" width="500px"></canvas>
  </div>
  
  <div class="astronaut__head">
    <canvas id="visor" width="60px" height="60px"></canvas>
    <div class="astronaut__head-visor-flare1"></div>
    <div class="astronaut__head-visor-flare2"></div>
  </div>
</div>
  </body>
</html>


================================================
FILE: _includes/layouts/base.njk
================================================
<!doctype html>
<html {% if section %} data-current="{{ section }}"{% endif %} lang="en">
  {% include "components/head.njk" %}
  <body>
    <div id="app" class="h-screen bg-white dark:bg-gray-900">
      {% include "components/header.njk" %}
      {% if site.navigationStyle == "vertical" %}
      <nav id="navigation" class="fixed inset-y-0 overflow-x-hidden overflow-y-auto  hidden w:64 lg:w-72 mt-20 md:block  dark:bg-gray-900  flex-none top-0  p-4 w-64 md:border-r border-b border-gray-100 dark:border-gray-800">
        <button  name="Close navigation" onclick="closeNavigation()" class="float-right justify-end block md:hidden -mr-4 p-6">	<svg xmlns="http://www.w3.org/2000/svg" class="dark:bg-gray-500" width="18" height="18" viewBox="0 0 18 18"><path class="text-gray-100" d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/></svg></button>
        <div class=" clear-right">
          <a href="/">
          <div class="block md:hidden pt-4 font-semibold text-gray-500">{{ site.name }}</div>
          <div class="block md:hidden text-sm text-gray-500 mb-4">{{ site.subtitle }}</div>
          </a>
          {{ collections.all | eleventyNavigation | eleventyNavigationToHtml({anchorClass: "item",activeAnchorClass: "active",activeListItemClass: "active",activeKey: eleventyNavigation.key, listClass: "nav", listItemClass: "item"}) | safe }}
        </div>
      </nav>
      {% endif %}
      {% if site.navigationStyle == 'horizontal' %}
        <main id="main" class="dark:bg-gray-900 pt-16 sm:pt-18 md:pt-48">
      {% else %}
        <main id="main" class="dark:bg-gray-900 pt-16 sm:pt-18 md:pt-20 md:pl-64 lg:pl-72">
      {% endif %}     
        {{ layoutContent | safe }} 
        {% include "components/footer.njk" %} 
        </main>
    </div>
  </body>
</html>


================================================
FILE: _includes/layouts/contact.njk
================================================
---
layout: layouts/base.njk
section: contact
---
<div class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl mt-8 pl-4 pr-4">
{% include "components/contact.njk" %}
</div>

================================================
FILE: _includes/layouts/page.njk
================================================
---
layout: layouts/base.njk
section: page
---

{% if site.navigationStyle == "vertical" %}
    {% if site.enableEditButton == true or site.enableDatestamp == true %}
    <div class="flex mt-4 justify-end mr-1">
        {% if site.enableDatestamp == true %}
        <div class="inline-block w-auto text-xs text-gray-500 pt-1 pb-1 pl-3 mr-2 rounded ">Updated <time datetime="{{ page.date | machineDate }}">{{ page.date | readableDate }}</time></div>
        {% endif %}
        {% if site.enableEditButton == true %}
        <div class="inline-block w-auto text-xs  text-gray-500 hover:bg-gray-200 pt-1 pr-4 pb-1 pl-3 rounded "><a target="_blank" rel="noopener" rel="noreferrer" href="{{ site.githubUrl }}/edit/{{ site.githubBranch }}/{{ page.inputPath }}">Edit</a></div>
        {% endif %}
    </div>
    {% endif %}
{% endif %}
 
<div class="flex w-full justify-center">
    <div class="mt-4 px-6 md:px-6 lg:px-8 xl:px-12 w-full max-w-5xl">
        <div class="wrapper flex justify-between">
            {% if site.enableTOC and toc %} 
            <div class="main flex flex-col pr-0 xl:pr-64 prose sm:prose lg:prose-lg xl:prose-md">
            {% else %}
             <div class="main  flex flex-col pr-0 prose sm:prose lg:prose-lg xl:prose-md">
            {% endif %}    
                <article>
                <h1 class="dark:text-gray-500">{{ title }}</h1>
                {% if site.navigationStyle == "horizontal" %}
                    {% if site.enableEditButton == true or site.enableDatestamp == true %}
                        <div class="flex mt-4 justify-end mr-1">
                        {% if site.enableDatestamp == true %}
                            <div class="inline-block w-auto text-xs text-gray-500 pt-1 pb-1 pl-3 mr-2 rounded ">Updated <time datetime="{{ page.date | machineDate }}">{{ page.date | readableDate }}</time></div>
                        {% endif %}
                        {% if site.enableEditButton == true %}
                            <div class="inline-block w-auto text-xs  text-gray-500 hover:bg-gray-200 pt-1 pr-4 pb-1 pl-3 rounded "><a class="text-gray-500 no-underline font-normal !important" target="_blank" href="{{ site.githubUrl }}/edit/{{ site.githubBranch }}/{{ page.inputPath }}">Edit</a></div>
                        {% endif %}
                        </div>
                    {% endif %}
                {% endif %}
                <div class="self-center adjust  dark:text-gray-400">
                    {{ layoutContent | safe }}
                    {% if (site.enableComments) and (comments !== 0) %}
                        <!-- Paste your comment code here! -->
                    {% endif %}
                    
                    {% if site.enablePageNavigation == true %}
                     <ul class="footer-nav pt-4 mb-8  ml-0 pl-0 flex flex-wrap flex-row-reverse  justify-between mt-8 list-none">
                     {%- set nextPage = collections.menuItems | getNextCollectionItem(page) %}
                     {%- if nextPage %}<li class="ml-0 pl-0"><a class="font-semibold text-gray-500 hover:text-gray-900" href="{{ nextPage.url | url }}">{% if nextPage.data.eleventyNavigation.title %}{{  nextPage.data.eleventyNavigation.title }}{% else %}{{  nextPage.data.eleventyNavigation.key }}{% endif %}</a> <svg height="24" fill="none" viewBox="0 0 24 24" stroke="#6B7280" class="inline ml-1 flex-shrink-0"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg></li>{% endif %}
                     {%- set previousPage = collections.menuItems | getPreviousCollectionItem(page) %}
                     {%- if previousPage %}<li class="ml-0 pl-0"><svg height="24" fill="none" viewBox="0 0 24 24" stroke="#6B7280" class="transform rotate-180 inline mr-1 flex-shrink-0">
                     <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
                     <a class="font-semibold text-gray-500 hover:text-gray-900" href="{{ previousPage.url | url }}">
                    {% if previousPage.data.eleventyNavigation.title %}{{  previousPage.data.eleventyNavigation.title }}{% else %}{{  previousPage.data.eleventyNavigation.key }}{% endif %} </a> </li>{% endif %}
                    </ul>
                    {% endif %}
                </div>
                </article>
            </div>    
            
            {% if site.enableTOC and toc %}
            <div class="text-gray-500 font-semibold hidden xl:block sidebar sticky right-0 mt-12 -ml-56 w-56 h-full top-48">
            <small>ON THIS PAGE</small><br />
                <aside class="font-normal">
                {{ content | toc | safe }}
                </aside>
            </div>
            {% endif %}
        </div>
    </div>
</div>
   



================================================
FILE: _includes/layouts/robots.njk
================================================
User-agent: * Disallow: /

================================================
FILE: admin/config.yml
================================================
backend:
  name: git-gateway
  branch: main # Branch to update (optional; defaults to main)

# Uncomment below to enable drafts
# publish_mode: editorial_workflow

media_folder: "static/img" # Media files will be stored in the repo under images/uploads

# # Cloudinary
# media_library:
#   name: cloudinary
#   config:
#     cloud_name: broeker
#     api_key: 159818251742281

collections:
  # Our blog posts
  # - name: "blog" # Used in routes, e.g., /admin/collections/blog
  #   label: "Post" # Used in the UI
  #   folder: "posts" # The path to the folder where the documents are stored
  #   create: true # Allow users to create new documents in this collection
  #   slug: "{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
  #   fields: # The fields for each document, usually in front matter
  #     - { label: "Title", name: "title", widget: "string" }
  #     - { label: "Publish Date", name: "date", widget: "datetime" }
  #     - { label: "Author", name: "author", widget: "string", default: "Anonymous" }
  #     - { label: "Summary", name: "summary", widget: "text" }
  #     - { label: "Tags", name: "tags", widget: "list", default: ["post"] }
  #     - { label: "Body", name: "body", widget: "markdown" }
  # Our pages e.g. About
  - name: "pages"
    label: "Page"
    folder: "pages"
    create: true # Change to true to allow editors to create new pages
    slug: "{{slug}}"
    fields:
      - { label: "Title", name: "title", widget: "string" }
      - { label: "Publish Date", name: "date", widget: "datetime" }
      - { label: "Permalink", name: "permalink", widget: "string" }
      - label: "Navigation" # https://www.11ty.dev/docs/plugins/navigation/
        name: "eleventyNavigation"
        widget: "object"
        fields:
          - { label: "Key", name: "key", widget: "string" }
          - { label: "Order", name: "order", widget: "number", default: 100 }
          - { label: "Parent", name: "parent", widget: "string", required: false, hint: "(Optional) Enter a matching parent key to set this a nested or child page" }
          - { label: "Title", name: "title", widget: "string",  required: false, hint: "(Optional) Enter alternate text for navigation link" }
      - { label: "Body", name: "body", widget: "markdown" }
  - label: "Globals"
    name: "globals"
    files:
      - label: "Site Data"
        name: "site_data"
        delete: false
        file: "_data/site.json"
        fields:
          - {label: "Site name", name: "name", widget: "string"}
          - {label: "Site subtitle", name: "subtitle", widget: "string", required: false}
          - {label: "Meta description", name: "description", widget: "string"}
          - {label: "Site footer", name: "footer", widget: "string", required: false}
          - {label: "Site Url", name: "url", widget: "string"}
          - {label: "Github Url", name: "githubUrl", widget: "string"}
          - {label: "Github branch", name: "githubBranch", widget: "string"}
          - {label: "Navigation style", name: "navigationStyle", widget: "string"}
          - {label: "Site emoji", name: "emoji", widget: "string", required: false}
          - {label: "Enable search", name: "enableSearch", widget: "boolean"}
          - {label: "Enable darkmode", name: "enableDarkMode", widget: "boolean"}
          - {label: "Enable edit button", name: "enableEditButton", widget: "boolean"}
          - {label: "Enable datestamp", name: "enableDatestamp", widget: "boolean"}
          - {label: "Enable Github link", name: "enableGithubLink", widget: "boolean"}
          - {label: "Enable contact form", name: "enableContact", widget: "boolean"}
          - {label: "Enable Netlify CMS", name: "enableNetlifyCMS", widget: "boolean", default: false}
          - {label: "Enable comments", name: "enableComments", widget: "boolean", default: false}
          - {label: "Enable encryption", name: "enableEncryption", widget: "boolean", default: false}
          - {label: "Enable page navigation", name: "enablePageNavigation", widget: "boolean", default: false}

================================================
FILE: admin/index.html
================================================
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Netlify CMS</title>
  </head>
  <body>
    <!-- Include the script that builds the page and powers Netlify CMS -->
    <script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
    <!-- Include Netlify Identity for authentication -->
    <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
    <script type="module" src="/admin/preview-templates/index.js"></script>
  </body>
</html>


================================================
FILE: admin/preview-templates/index.js
================================================
import Page from "/admin/preview-templates/page.js";
// import Post from "/admin/preview-templates/post.js";

// Register the Post component as the preview for entries in the blog collection
CMS.registerPreviewTemplate("pages", Page);
// CMS.registerPreviewTemplate("blog", Post);

CMS.registerPreviewStyle("/style.css");

// Register any CSS file on the home page as a preview style
// fetch("/")
//   .then(response => response.text())
//   .then(html => {
//     const f = document.createElement("html");
//     f.innerHTML = html;
//     Array.from(f.getElementsByTagName("link")).forEach(tag => {
//       if (tag.rel == "stylesheet" && !tag.media) {
//         CMS.registerPreviewStyle(tag.href);
//       }
//     });
//   });


================================================
FILE: admin/preview-templates/page.js
================================================
import htm from "https://unpkg.com/htm?module";

const html = htm.bind(h);

// Preview component for a Page
const Page = createClass({
  render() {
    const entry = this.props.entry;

    return html`
      <main class="prose p-4">
        <h1>${entry.getIn(["data", "title"], null)}</h1>
        <div class="adjust">
        ${this.props.widgetFor("body")}
        </div>
      </main>
    `;
  }
});

export default Page;


================================================
FILE: admin/preview-templates/post.js
================================================
import htm from "https://unpkg.com/htm?module";
import format from "https://unpkg.com/date-fns@2.7.0/esm/format/index.js?module";

const html = htm.bind(h);

// Preview component for a Post
const Post = createClass({
  render() {
    const entry = this.props.entry;

    return html`
      <main class="prose p-4">
        <article>
          <h1>${entry.getIn(["data", "title"], null)}</h1>
          <div class="inline-block w-auto text-xs text-gray-500 pt-1 pb-1 pl-3 mr-2 rounded ">Updated <time datetime="{{ date | machineDate }}">{{ date | readableDate }}</time></div>
          <p>${entry.getIn(["data", "summary"], "")}</p>
          <div class="adjust">
          ${this.props.widgetFor("body")}
          </div>
          <p>
            ${
              entry.getIn(["data", "tags"], []).map(
                tag =>
                  html`
                    <a class="text-xs mr-2 px-3 py-2 text-gray-600 bg-gray-200 no-underline rounded" href="#" rel="tag">${tag}</a>
                  `
              )
            }
          </p>
        </article>
      </main>
    `;
  }
});

export default Post;


================================================
FILE: content/buy.md
================================================
---
permalink: false
eleventyNavigation:
  key: Get the eBook edition
  order: 10
  url: https://leanpub.com/proseforprogrammers
---

================================================
FILE: content/pages/contact.md
================================================
---
layout: layouts/contact.njk
title: Send a message 
section: contact
date: Last Modified
permalink: /contact/index.html
---
You can use [Netlify Forms](https://www.netlify.com/docs/form-handling/) to create contact forms like this one, or any other custom forms you may wish to create. All submissions are sent directly to your Netlify dashboard (with optional notifications.) All forms utilize Netlify's native spam filter will display a CAPTCHA for any flagged submissions.


================================================
FILE: content/pages/home.md
================================================
---
title: About this book
permalink: /
eleventyNavigation:
  key: About this book
  order: 0
---

_Prose for Programmers_ is a book for programmers.
It is a guide to mastering the most difficult programming language of all: human language.

Just as with programming,
we write with particular goals in mind.
That goal might be as simple as reminding someone about a requirement
or as complex as justifying an entirely new architecture for a product.
But in every case we write in order to achieve something.

This book will teach you how to achieve those goals more effectively with clearer, more persuasive writing.

## It is a work in progress

The first three chapters are content complete.

* [Why should developers study writing?](/manuscript/why)
* [General Rules](/manuscript/rules)
* [The Writing Process](/manuscript/process)

And there are four chapters to come.

* Writing Structures
* Audience
* Genres
* Other Resources

## It is editable on GitHub

The manuscript of _Prose for Programmers_,
as well as the code for the website is hosted on [GitHub](https://github.com/joshuacc/prose-for-programmers).
Each chapter has a convenient edit link at the top of the page.
If you see any mistakes, typos, or other issues,
feel free to edit and open a pull request.

## It is available in eBook form for your Kindle or other e-reader

You can get the EPUB/MOBI/PDF version of _Prose for Programmers_ [via Leanpub](https://leanpub.com/proseforprogrammers).

================================================
FILE: content/pages/pages.json
================================================
{
  "layout": "layouts/page.njk"
}


================================================
FILE: filters/searchFilter.js
================================================
const elasticlunr = require("elasticlunr");
const emojiRegex = require('emoji-regex/RGI_Emoji.js')

module.exports = function (collection) {
  // what fields we'd like our index to consist of
  var index = elasticlunr(function () {
    this.addField("title");
    this.addField("content");
    this.setRef("id");
  });

  // loop through each page and add it to the index
  collection.forEach((page) => {
    index.addDoc({
      id: page.url,
      title: page.template.frontMatter.data.title,
      content: squash(page.templateContent),
    });
  });
  
  function squash(text) {
    const regex = emojiRegex();
    var content = new String(text);

    // all lower case, please
    var content = content.toLowerCase();
  
    // remove all html elements and new lines
    var re = /(.*?&lt;.*?&gt;)/gi;
    var plain = unescape(content.replace(re, ''));
  
    // remove duplicated words
    var words = plain.split(' ');
    var deduped = [...(new Set(words))];
    var dedupedStr = deduped.join(' ')
  
    // remove short and less meaningful words
    var result = dedupedStr.replace(/\b(\.|\,|\<;|the|a|an|and|am|you|I|to|if|of|off|me|my|on|in|it|is|at|as|we|do|be|has|but|was|so|no|not|or|up|for)\b/gi, '');
    //remove newlines, and punctuation
    result = result.replace(/\.|\,|\?|<p>|-|—|\n/g, '');
    //remove repeated spaces
    result = result.replace(/[ ]{2,}/g, ' ');
    // remove most emoji
    result = result.replace(/([#0-9]\u20E3)|[\xA9\xAE\u203C\u2047-\u2049\u2122\u2139\u3030\u303D\u3297\u3299][\uFE00-\uFEFF]?|[\u2190-\u21FF][\uFE00-\uFEFF]?|[\u2300-\u23FF][\uFE00-\uFEFF]?|[\u2460-\u24FF][\uFE00-\uFEFF]?|[\u25A0-\u25FF][\uFE00-\uFEFF]?|[\u2600-\u27BF][\uFE00-\uFEFF]?|[\u2900-\u297F][\uFE00-\uFEFF]?|[\u2B00-\u2BF0][\uFE00-\uFEFF]?|(?:\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDEFF])[\uFE00-\uFEFF]?/g, '');
   
    let match;
    while (match = regex.exec(result)) {
      const emoji = match[0];
      result = result.replace(emoji,' ')
    }
  
    return result;
  }

  return index.toJSON();
};

================================================
FILE: loader.js
================================================
if (localStorage.getItem('password')) {
    insertPlainHTML(atob(localStorage.getItem('password')))
} else {
    document.getElementById('staticrypt-form').addEventListener('submit', function(e) {
        e.preventDefault();
        insertPlainHTML(document.getElementById('staticrypt-password').value);
    });
}

================================================
FILE: manuscript/Book.txt
================================================
why.md
rules.md
process.md

================================================
FILE: manuscript/manuscript.json
================================================
{
  "layout": "layouts/page.njk"
}


================================================
FILE: manuscript/process.json
================================================
{
  "title": "The Writing Process",
  "eleventyNavigation": {
    "key": "The Writing Process",
    "order": 3
  }
}

================================================
FILE: manuscript/process.md
================================================
# The Writing Process

Many people are under the impression that writing is a mystical
-- or at least mysterious --
activity, governed entirely by inspiration.
While it is true that some authors work this way,
waiting for inspiration to strike isn't a common strategy among professional writers,
even novelists.

For programmers and others writing non-fiction in a business context,
there is a simple process that yields quality results.

1. Determine the goal
2. Determine the audience
3. Choose a suitable structure
4. Build an outline
5. Turn the outline into prose
6. Revise obvious weaknesses
7. Obtain feedback
8. Revise based on feedback
9. Publish

In this chapter, we will walk through what each of these steps entail.

## 1. Determine the Goal

### Why am I writing this, anyway?

Every time you start writing something,
ask yourself, "Why?"
Sometimes the answer is obvious,
but often it's not.
And even on those occasions when it is obvious,
asking the question helps keep you focused on the goal.

It's important to remember that we're trying to find the purpose of the document,
not your personal motivation for writing it.
If your answer to "Why am I writing this?" includes the words "I" or "me,"
it is worth evaluating whether you're really thinking of the document's goal.

For instance, if the answer to why you are writing a tutorial is,
"So that I can get a raise,"
you're thinking of the wrong thing.
It's not that the motivation is _wrong_.
It's just _irrelevant_ to producing a good tutorial.
A better answer is,
"To help others improve their game's performance."

Because this answer is focused on what the audience receives,
it is actually useful for guiding your writing efforts.

### Keep asking why.

Often, the first goal we come up with will be superficial.
"Teaching others how to use object pools,"
may be accurate,
and even somewhat useful,
but a bit more context can be even more useful.

There is a technique for root cause analysis called [5 Whys][],
the premise of which is that you need to ask "Why?" multiple times to find the root cause of a problem.
This technique also works well for determining a useful goal for writing.

Suppose the first goal that occurs to you is
"Teach developers how to use object pools."
That is a perfectly clear and useful goal.
But if you write only to that narrow goal,
your readers won't know why the technique is useful.
So ask yourself, "Why object pools?"

Perhaps the answer is "To improve performance."
But again we can ask "Why?"
And our answer is something like, "To keep gameplay smooth."

At this point we can state the goal as:
"Teach game developers how to keep their games running smoothly by using object pools."

With this more robust goal in mind we're more likely to communicate,
not just the technique,
but also its importance and trade-offs.

## 2. Determine the Audience

Once we understand the goal of the document,
we have enough information to start considering our audience.
Our first question, though, takes a step back from the goal.

### Who will be reading this?

Often the readership of documents is wider than we would initially expect.
A framework's website will be read by developers, of course,
but perhaps also:

* Architects evaluating technology choices
* Managers trying to keep abreast of what their team is using
* Technical recruiters trying to understand the skillset they are hiring for
* Designers trying to understand technical constraints on their designs
* Sysadmins figuring out the framework's deployment features

Take time to list the different types of potential readers.
Doing so will help draw out whether additional supporting material is needed.

### Which readers are primary?

Once you have the list of potential readers,
refer back to the goal to decide which readers are most important.
In most cases there will be a single group of readers who constitute your primary audience.

Continuing the previous example:
the primary audience of a framework website is developers.
Others are important, but secondary to that audience.

### What is their relation to the subject?

At this point we have our primary audience,
but haven't given much detail about them.
So it's time to consider their relation to the subject.

One important axis to consider is their current level of expertise.
Are they absolute beginners,
world class experts,
or somewhere in between?
The answer to this question drives
which sorts of things can be assumed
and which need to be explained in great detail.

Another axis is the readers emotional relationship with the subject.
Are they intimidated, enthusiastic, or something else?
A developer who fled Java for Ruby will have a very different attitude toward Haskell than a brand new developer.
They may both be fearful,
but about different things.
Those emotional concerns also need to be addressed.

### What is their relation to you?

In addition to the subject,
we must also consider how the audience is related to you,
the author.
Are they coworkers?
Then you can make certain assumptions about their familiarity with the business.

Are you on friendly terms or is some of the audience hostile?
You may need to focus more on your logical case for the latter.

Are you part of the same culture/subculture?
"Corporate suits" probably won't find image macros as amusing as your 22-year-old developer buddy.

[5 Whys]: http://en.wikipedia.org/wiki/5_Whys

## 3. Choose a Suitable Structure

Now that we know the goal and the audience
we can consider how to structure the document to serve them.

### Choosing a structure based on the goal

Goals can generally be placed on a spectrum between purely informational and purely persuasive.
On one end of the spectrum,
topical hierarchies are great at communicating lots of information efficiently.
The hierarchy conveys the big picture while also supporting scanning through subtopics.

At the other end of the spectrum,
narrative is particularly effective at persuading people by engaging our natural sense of empathy.
Telling a child that "Lying is a bad idea," is less likely to change their behavior than hearing _The Boy Who Cried Wolf_.
For an example closer to our industry, consider _The Phoenix Project_:
it vividly illustrates the problems of bureacratic IT departments
as well as the benefits of embracing the DevOps movement.

### Choosing a structure based on the audience

Your structure also needs to be suited to your audience.

If your primary audience is developers with deep experience in your topic,
then you may want to address them with an equally deep and detailed hierarchy of topics.
A beginner, however, would likely benefit from a shallower step by step "recipe" guide.

The reader's level of interest factors into which structure is best suited.
A highly motivated reader may want to get right to the heart of the matter.
But a distinterested reader
--who is only looking at your document because his boss made him--
may need to be enticed with a joke or anecdote explaining what is in it for him.
That sort of reader will probably also need frequent positive reinforcement.

The audience's relationship with you also makes a difference.
If they are familiar with you and trust your judgment,
they are more likely to be patient waiting for a payoff.
But if they don't know anything about you,
the internet is only a click away.

### Mixing and matching

Most writing structures are relatively flexible
and can be combined with other structures as warranted by the goal and the audience.
**Don't be afraid to mix and match them.**

For example, suppose that you need to convince business leaders that
it is worthwhile to break your large monolithic application into several separate services.
You'd probably start with a Problem-Solution structure like this:

* Problems with the current architecture
* Proposed solution

In order to help the businesspeople make a decision, you'd probably extend it with a Pro-Con analysis as well.

* Problems with the current architecture
* Proposed solution
* Costs and risks of change
* Costs and risks of not changing

A simple factual elaboration on that outline may win intellectual assent,
but is unlikely to elicit wholehearted support from non-technical leaders.
Why?
Because they lack experiential knowledge of the problems, solutions, and risks.

But parables and other metaphorical narratives can impart a degree of experiential knowledge without requiring actual experience.
What sort of narrative would work in this case?

Perhaps a story of a military building a single gigantic ship.
It is powerful,
but difficult to maneuver,
and if it somehow fails then its entire arsenal is useless.
(Business books are fond of military metaphors for some reason.)
Then contrast this with building a fleet of small independently maneuverable ships.

Revising the outline to incorporate this narrative gives us:

* Problems with the current architecture
    * Parable of the dreadnought
    * Supporting factual details
* Proposed solution
    * Parable of the fleet
    * Supporting factual details
* Costs and risks of change
    * Refer to parable
    * Supporting factual details
* Costs and risks of not changing
    * Refer to parable
    * Supporting factual details

Combining these three structures creates a much more powerful and persuasive account than any of them alone.

## 4. Build an Outline

With a structure
(or set of structures)
chosen for your project,
it is time to build an outline.
Some structures,
like API documentation,
may come with an outline template to follow.
But many others,
like the inverted pyramid,
do not.

This phase is all about taking your chosen structures
and combining them with the details of your subject
to produce the skeleton of your document.

The outline itself is a simple hierarchical list of
the things you want to communicate
and the approach you want to take at each step.
Particularly for smaller projects,
the line between choosing a structure and outlining can be rather blurry.
(Evidenced by the the section on choosing structures ending with an outline!)
But an outline should show in detail **how** you intend to implement your chosen structure.

### How detailed should an outline be?

This will likely vary from person to person,
but I prefer to outline in enough detail that
each low level bullet point corresponds to one or two paragraphs in the final text.
This gives me sufficient guidance that I don't get lost,
but also gives me enough flexibility to expand on some points without deviating from the outline.

### How do you actually create the outline?

There are two basic approaches:
depth-first
and breadth-first.
A depth-first approach starts at the top
and explores all the way down to the details one point at a time.
A breadth-first approach starts at the highest level
and only becomes more detailed after all the higher level points have been filled in.

**Partially completed depth-first outline**

* Point 1
    * Point 1.1
        * Point 1.1.1
        * Point 1.1.2
        * Point 1.1.3

**Partially completed breadth-first outline**

* Point 1
* Point 2
* Point 3
* Point 4
* Point 5

Breadth-first generally produces better results,
because it provides more overall context each time we step down a level of detail.
That extra context makes it much less likely that we will get stuck on trivia.

### Evaluate and revise

Once you've drafted your outline,
be sure to evaluate it with your goals and audience in mind.
It is much easier to move or edit a few bullet points
than to revise entire sections of your document.

## 5. Turn the outline into prose

Everything we've talked about so far has been aimed at preparing you for this part of the process.
You should know your goal, audience and structure.
And your outline gives you a game plan.
Now it's time to turn all that into something for others to read.

### Putting flesh on the bones

Right now what you've got is a skeleton.
It's vaguely in the shape you want,
but if it tried to walk around it would quickly fall apart.
There are two principle things that it is lacking:
muscle to give it strength,
and ligaments to hold things together.
Let's start with the muscle.

Bare statements of fact rarely carry much weight with readers,
regardless of how important those facts might be.
They have to be elaborated on
and placed into their proper context.
And usually the most important context for a reader is the answer to 
"Why do I care about this?"
A reason to care gives your prose strength.

Consider this example:

> Continuous integration improves the development process.

> Continuous integration improves the development process by helping us detect defects sooner.
> That means fewer late night calls to debug production.

The latter includes more supporting detail about _how_ it helps,
as well as a reason for developers to care about it.

In addition to muscle we need ligaments.
The ligaments are what actually connects one bone to another.
The prose equivalent is a transition.

Sudden jumps from one topic to another
(even closely related)
are jarring.
Transitions serve to smooth that over by
clearly signaling the close of one topic,
the introduction of the next,
and possibly how they are related.

A transition can be a simple phrase.
It can be an entire sentence.
It can be a paragraph.
Or multiple paragraphs.
It all depends on the size of the pieces you're transitioning between.

For an example, take a look at the paragraph that started this section (#5) above.

### It's not necessarily exposition

When working from your outline,
beware of always choosing the most direct expansion of your points.
Simple exposition is the most common way of describing things,
but it isn't always the best.
Using other forms of expansion will give your prose variety and help maintain reader interest.

Especially in a longer text,
including the occasional story, joke, or anecdote
can make the difference between a pleasant read or an unbearably boring one.

Whatever you choose to use,
it should be integrally related to the points you're trying to make.

There is a story about a high school student who had to give a book report.
When he stood up in front of the class he yelled,
"Sex! Sex! Sex!"
then continued,
"Now that I have your attention,
I'd like to talk to you about The Complete History of World War II."

Don't be like the high school student.

## 6. Revise

At this point you have a fleshed out draft,
so now it is time to fix the gaps and rough edges.

### Evaluate

Starting at the beginnning, read each unit of your work.
Depending on the length of your document, that unit might be a paragraph, section, or chapter.
As you read, ask yourself the following questions:

* Does this advance the goal?
* Is it clear?
* Is it concise?
* Is it well organized?
* Is it scannable?

You'll likely want to read the unit multiple times to answer these questions.
For a longer unit, one read per question -- or even more -- may be appropriate.

> #### Tip: Try different contexts
>
> Reading in a different context than you usually write in can help you to see things differently and spot problems more easily.
> For example, if you usually write at a computer,
> try printing your document out on paper,
> or even reading it out loud.

### Plan your corrections

Take notes on each thing that needs to be improved,
but don't make corrections right away.
Introducing a slight delay between identifying the problem and attempting to fix it
will give your mind a chance to work on the problem,
and often arrive at a better solution than the first one you thought of.

Your notes can take many forms:
a separate text file,
using Track Changes in Microsoft Word,
and so on.
I tend to prefer annnotating a physical paper with pen or pencil,
but you should experiement and find what works best for you.

After you have a list of all the corrections you plan to make,
you may be able to spot common patterns for future improvement.
But the immediate task is to begin making corrections.

### Make your corrections

Proceed through your notes from beginning to end,
correcting each problem.
If you get stuck on something,
leave it to the side for now.
You'll come back to it later.

Once you've completed all the corrections you were able to make,
take another look at any you left behind.
You may know how to fix them now.
If so, go ahead.
If not, we'll leave them for the next stage of the writing process: feedback.

## 7. Get feedback

Up to this point everything has come from you,
even thinking about the audience.
But this is where you actually engage with other people for the first time.
Brace yourself,
because feedback from real people is almost certain to suprise you,
regardless of whether it's positive or negative.

### Sources of feedback

Generally you will be getting feedback from one of three types of readers:
members of the target aurdience,
experts on the subject,
and friends/coworkers.
Each of these groups has strengths and weaknesses.
Getting feedback from all three is ideal,
and occasionally the groups overlap,
but feedback from any of them is useful for checking your assumptions.

#### Target audience

Members of the target audience are extremely valuable
because they can tell you whether your writing is achieving the intended goal.
Unfortunately, they often can't tell you why.
And even if they do offer a reason,
it shouldn't always be taken at face value.

To help overcome this issue,
it can help to supply a list of specific questions for them to answer.
For example:

* By the end did you think that it is worth investigating the new technology I described?
* If not, was there a specific point where I lost you?
* Did any of my supporting evidence seem questionable?

Unfortunately, it can be difficult or impossible to get feedback from the audience.
After all, when drafting an email to your boss's boss,
you can't very well ask them to proofread it.

#### Experts

Expert feedback can also be very useful,
particularly for identifying inaccuracies and mistakes.
One issue to watch out for, though,
is that experts may not remember what it was like to be a beginner.
So if beginners and non-experts are in your target audience,
be careful of suggestions to includes lots of additional details which they won't have the context to understand.

#### Friends and coworkers

Friends and coworkers are often the easiest reviewers to obtain.
Unfortunately, they are often the least reliable in providing needed correction.
No one wants to hurt the feelings of someone they like.
But they are often excellent at providing encouragement,
and when working on a large writing project,
that can be invaluable.

### Evaluating feedback

When evaluating the feedback there are a few principles to keep in mind.

First, _readers are always right about their reactions_.
If a reader says that they were confused by a certain point,
then they were,
no matter how clear it may appear to you.

Second, _a reader's negative reaction does not automatically lead to revision_.
It is your responsibility to evaluate
whether issues raised are actually problems for your target audience
and serious enough to require addressing.

Third, _a reader's proposed solution may not be correct_.
Sometimes readers will suggest solutions without explaining the problem they identified.
For example, "I think you need a fancy graphic right here."
This suggestion might be a good one,
but you should work to understand the underlying problem.
Perhaps the text became boring,
or perhaps a diagram would help with clarity.
Whatever the case may be,
don't just take the suggestion at face value.

Finally, _a reader's feedback should be weighted differently based on who they are and what area their feedback is in._
An expert's feedback regarding correctness should be prioritized over a beginner's,
and an audience member's feedback regarding readability should be prioritized over the expert's.

### Compile the feedback into revision notes

After taking all of this in,
you should have a list of changes to make.
And you may also have a list of positive points which you should try to retain.
Despite the focus on improving problems,
you also don't want to edit away something that is working.

## 8. Revise based on feedback

Just like with your first revision notes,
you will go back through and make your changes one at a time,
evaluating the criteria of clarity, concision, organization, scannability and goal focus.
By the time you are done,
you should have a pretty solid draft.

As you revise, make changes one at a time,
assessing clarity, concision, organization, scannability, and goal focus.
Pay attention to common themes in the feedback you receive.
Those are the ones most likely to require revision.

Remember to prioritize the most significant changes first.
Tackle issues like organization or persuasiveness before minor details like grammar.
Getting something grammatically correct is a waste of time
if that entire section will be reworked or removed.
Throughout revision, continuously review your goals and audience,
and be prepared for multiple rounds of changes.

After you've revised, seek additional feedback,
both to verify initial concerns are addressed
and to ensure no new problems have been introduced.
Consult with both original and new readers to get different perspectives.
Remember, continuous revision is crucial for producing a polished and effective piece of writing.

## 9. Publish

Publishing your work is usually the most critical part of the writing process,
but remember, it can happen at any point, depending on the project.
In some cases, publishing might be part of an ongoing process,
with updates and revisions made after the first release.
This is especially true for things like wiki-based technical documentation
or presentations that are given multiple times and need tweaks based on audience feedback.

Before you publish, think about the context and the level of polish your writing needs.
Not every situation calls for super polished writing.
A quick email to a coworker won't need the same level of refinement as a formal report for the big boss.
Adapt the level of polish to the context so you strike the right balance between quality and getting things done.

Before hitting that publish button,
give your work a once-over for any errors or inconsistencies,
and make sure your formatting looks good,
based on what's required in your situation.
This final check is crucial for presenting your writing in the best light.

After publishing, don't forget to spread the word.
Share your work with the people who need to see it,
whether that's passing a report to colleagues,
posting a blog on social media,
or presenting your findings at a conference.
Keep in mind that publishing doesn't mean you're done with revisions.
Be open to feedback even after your work is out there,
and be ready to make updates or corrections as needed.
Embracing this back-and-forth will help you keep improving your writing skills
and adapt to the needs of your audience in all kinds of situations.

## Summing it up

The writing process is all about striking the right balance between the formal process and the needs of the situation.

From setting goals and figuring out who you're writing for,
to making an outline,
revising,
and hitting that publish button,
each step is important.
But not equally important.

Keep an eye on your context and switch things up as needed,
whether it's the level of polish or the amount of feedback and revision you're going for.
Don't forget, the process can and should be trimmed down when it makes sense for the situation,
helping you focus on what's most important in each specific context.
The more you work at it, the better you'll get at writing and connecting with your audience.

Writing takes time, practice, and being open to learning from both the things you nail and the things you, well, don't.
As you gain experience as a writer,
you'll find that this whole process starts to feel more natural,
and your ability to get your point across will only get better.
Just keep writing, learning, and tweaking your process to fit each unique writing situation you find yourself in.

================================================
FILE: manuscript/rules.json
================================================
{
  "title": "General Rules",
  "eleventyNavigation": {
    "key": "General Rules",
    "order": 2
  }
}

================================================
FILE: manuscript/rules.md
================================================
# General Rules

Rules.
The very word can trigger skepticism.
And understandably so.
We are often confronted with seemingly arbitrary rules
which make our lives harder for no apparent reason.

In this chapter I will give you rules to follow,
but I will also explain each rule's rationale.
So if you find yourself in a situation beyond the scope of the rule,
you'll be equipped to make intelligent decisions about how or if to apply it.

It is also important to acknowledge the limits of these rules.
They are targeted principally at writing within a professional business context.
If you are writing a novel,
these rules will be of limited use.
And if you are writing poetry,
they will probably be entirely useless.
But if you need to communicate ideas to your coworkers,
then these rules will serve you well.

Now that we've set the appropriate context, let's look at those rules.

## 1. Be goal oriented.

> "In my end is my beginning."
> 
> -- T.S. Eliot

### Everything you write has a goal

Suppose a boss or client came to you and said they needed you to build a web application.
Your first question would probably be,
"What is the application for?"
The very idea of writing an application without knowing its purpose is ridiculous.
Yet when it comes to writing prose,
we too often fail to ask the obvious question.

Everything you write has a goal,
whether explicit or implicit.
Those API docs?
The stated goal is to inform other developers how to use your library.
That weekly status report you email your client?
The unstated goal is reassuring them that their money is being well spent.

The goal of a document isn't always obvious,
but in the next chapter, we'll look at some tools to figure it out.

### Knowing the goal equips you to fulfill it.

Let's go back to our hypothetical web application above.
Suppose you did try to build it without actually knowing its purpose?
You are just given a list of features and told to start building.
What would happen?

You'd have no way to evaluate how well or poorly the application was doing.
And that's not just a problem at the end.
All along you'd have questions about how best to build each feature.
But without knowing the application's purpose,
you'd have no way of choosing between the alternatives.
That kind of uncertainty is crippling.
And I suspect that is why many people avoid writing.

But when you do know the goal you are writing toward,
decisions are easier,
and it is simpler to tell whether your writing succeeds in achieving its end.

### The structure of goals

We've talked about goals a lot,
but so far we haven't really clarified what kind of goals we're talking about.
It's all about the document.

In this book,
when I talk about goals,
I mean the purpose intrinsic to the document[^telos],
not the author's personal motivations.
For instance,
your goal in writing API docs may be to keep your tech lead from bugging you about it yet again.
But the purpose of the docs themselves is to assist users of the API.

A document's goals will generally fall into one of two categories:
to inform or to persuade.
Most documents will include a bit of both,
but one will be primary.
To go back to the API docs example,
it may help persuade people to use your library,
but the primary purpose is just to inform them about how to do so.

[^telos]: If you're familiar with ancient Greek philosophy,
this is what Aristotle would call the document's _telos_.

## 2. Be concise.

In a professional context,
everyone is pressed for time.
Being concise is about respecting your readers' time.
Don't force them to read a page when a paragraph will do.

Conciseness also helps you achieve your goal.
The more time investment your text requires,
the more reluctant readers will be to give it to you.
So we should do our best to keep the required investment to a minimum.

At the same time, conciseness does not mean being excessively terse.
Conciseness means saying the exact amount necessary to achieve the goal:
no more and no less.

## 3. Be Clear

### Avoid ambiguity

Human language is rife with ambiguity.
Take as an example my first title idea for this book:
"Writing for Developers."
Only three words,
yet it could be read in two totally different ways.
That title could have meant
-- as I intended --
"A book for developers on the subject of writing."
But another equally reasonable interpretation was,
"A book on writing for an audience of developers."

Ambiguous language is useful in art and poetry,
but not when we are trying to communicate as efficiently as possible.

### Avoid jargon

It is also wise to avoid unfamiliar terms and jargon.
Of course, this is dependent on your audience.
To a developer, this sentence makes perfect sense:

> "Hoth is a lightweight MVVM framework
> which leverages dirty checking
> and immutable data structures for performance."

To a non-developer,
it sounds more like this:

> Hoth is a lightweight magic framework
> which leverages scary magic
> and scarier magic for performance.

For readers unfamiliar with the terminology it communicates next to nothing.

### Narrow the scope of your words

We love to speak in generalities.
Perhaps because it is difficult to definitely prove a generality wrong.
The problem is that the more general a statement is,
the more difficult it is to understand and apply.
Consider this example:

> Object-oriented programming is terrible.

It's not completely meaningless,
but it is extremely broad,
and the implications are unclear.
Should readers avoid all object oriented languages?
It's hard to tell what the author is thinking.

Contrast with this:

> Class-based inheritance tends to make code unnecessarily complex.

This version has narrowed the scope in two ways.
It has narrowed from all object-oriented programming to class-based inheritance.
And it has narrowed from "terrible" to "tends toward unnecessary complexity."
As a result,
this sentence is much easier to understand and evaluate.

Given the choice between the specific and the general, choose the specific.

## 4. Be organized.

For many people this is the most difficult part.
They know the goal.
They can write clear, concise sentences.
But they have a hard time putting those pieces together into a coherent whole.

In the next chapter we'll look at how to do it.
For now let's explore why.

### Structure aids comprehension

The human mind is built for pattern matching.
And it is pretty good at its job,
otherwise we'd have all been eaten by tigers long ago.

Consider two different lists:

* Apple pie
* Blueberry pie
* Cranberry pie

* Wrench
* Justice
* Puppy

The first list is much more memorable and comprehensible.
Why?
Because it is organized into a structure that our minds can easily extract.
Each item in the list was a pie.
Each of the pies was fruit-based.
And the pies were alphabetically ordered.
But the second list had no unifying organizational structure.

### Structure is fractal

Writing structures are simultaneously high level and low level.
They encompass everything
from your three main points
to that sub-sub-sub-point in paragraph 45.
And if you've chosen your structure well,
the micro and macro levels will tend to mirror each other.
For example, consider this outline for an article on the Hoth Framework.

> * Intro to Hoth
> * Immutable data structures
>   * Intro
>   * Benefits
>   * Examples
> * Dirty checking
>   * Intro
>   * Benefits
>   * Examples
> * Example application
> * Conclusion

Both of the main sub-points follow the same structure as the outline as a whole.
The mirroring isn't always this obvious,
but it is generally present.

### Projects may come with built-in structure

Certain kinds of projects come with ready-made,
very detailed structures,
simply because of how common that type of project is.
API docs,
for example,
may vary a bit by language,
but will generally look pretty close to this.

* Modules
  * Name
  * Description
  * Classes
    * Name
    * Description
    * Methods
      * Name
      * Arguments
      * Return value
      * Description

Not every predetermined structure is that specific, though.
Library websites have several things they need to include,
like language,
purpose of the library,
and how to install it,
but they also have more flexibility.

### General purpose structures

In addition to these very specific structures,
there are also many general purpose structures.
These are things like the inverted pyramid, objection-response, etc.

Typically you will pick two or three of these general purpose structures
and use them as the organizing principle of the project.

## 5. Be scannable.

While the previous rule was about the conceptual structure of a text,
this rule is about the visual typographic structure.
The point of scannability is to allow readers to locate important information at a glance,
rather than reading every word on the page.
Even for readers that do read every word,
scannable typography makes it easier to refer back to key ideas in the text.

### Break text into bite-sized pieces

Have you ever read a paragraph that took up an entire printed page?
They are no fun to read.
With no visual breaks,
it is difficult to keep track of where you are,
much less follow the flow of ideas.

To maximize scannability,
text should be broken into bite-sized units of thought.
Paragraphs, for example, should generally be 2-5 sentences long.
Much longer and it becomes difficult to read.
Shorter and you may want to use a heading instead.

### Divide and conquer with headings

The primary purpose of headings is to make your text's conceptual structure explicit.
However, they also have two important scannability implications.

First, headings provide a visual anchor allowing readers to locate where a concept is discussed.

Second, headings breaks the text into units of progress which help the reader stay motivated to continue.
This is part of the reason list posts are such a popular article format.
Each time the reader reaches the next heading,
they get to cross something off their mental checklist.

### Use lists for your lists

While not as common as paragraphs,
the humble list is one of the author's most helpful tools.
It provides:

* Visual attraction for the list
* Visual attraction for each item in the list
* A break from the monotony of paragraphs
* A sense of progress

You can, of course, embed lists directly into your sentences and paragraphs.

> While not as common as paragraphs,
> the humble list is one of the author's most helpful tools.
> It provides:
> visual attraction for the list,
> visual attraction for each item in the list,
> a break from the monotony of paragraphs,
> and a sense of progress.

However, doing so relegates the list contents to a secondary status.
In general,
using typographic lists
(bulleted, numbered, etc.)
for your conceptual lists makes sense if the exact contents are worth emphasizing.

### Emphasize key ideas with bold or italics

Ordinary paragraphs are the parts of a text most likely to be skipped over.
But sometimes important ideas only make sense as part of a paragraph.
That's where typographic emphasis comes in.

Highlighting important ideas with bold or italics allows readers to see key ideas at a glance.

However, there are some rules of thumb to keep in mind.

* Only highlight one phrase or sentence per paragraph.
* Don't highlight something in _every_ paragraph.
* **Never** highlight something with both bold and italics.

Go beyond these limits and readers are likely to feel that you are shouting at them.

================================================
FILE: manuscript/why.json
================================================
{
  "title": "Why should developers study writing?",
  "eleventyNavigation": {
    "key": "Why should developers study writing?",
    "order": 1
  }
}

================================================
FILE: manuscript/why.md
================================================
# Why should developers study writing?

If you've picked up this book, then you're probably a software developer.
Furthermore, you're someone who cares about your career.
You've probably spent countless hours
studying manuals,
reading library docs,
learning how to write clean code,
and otherwise mastering your craft.

But you may be skeptical.
There are a thousand other things you could learn.
Why spend the time studying writing?
Let's walk through some of the things you might be thinking.

## I'm paid to code, not to write.

In most cases, developers aren't paid to code.
We are paid to produce working software that solves a business problem.
In order to do that we may need to:

* Email a product manager to clarify a requirement
* Instant message a designer to verify the color and placement of a call-to-action button
* Document the details of an internal REST API
* Submit a patch to an open-source project you use
* File a bug report about a part of the software developed by another team
* Provide explanatory notes about that spike in server errors on the 14th
* Tweak the wording of some UI text that is confusing users,
  since the designer is unavailable

All of these things are run of the mill tasks for a software developer.
And every single one of them is a form of writing.

## I already know how to write. I did well in school.

Unfortunately, literacy,
even at college or university level,
doesn't guarantee the ability to write effectively.
Many degree programs place little emphasis on writing.
And those that do emphasize it often encourage an academic style which doesn't mesh well with most developer's jobs.

Academic writing tends to value a kind of distant objectivity which can manifest as:

* Unnecessary jargon
* Verbose and indirect prose
* Extreme formality

In contrast, most developers would benefit from making their prose clear, direct, and concise.

## But I could be studying the Hoth framework instead. It's so cool!

Yes, no matter which topic you decide to study,
you will be passing up something else.
But writing deserves special consideration.
Why?
**Because writing is a durable skill.**

Technology changes quickly.
If you're not careful,
you can easily invest a lot of time and energy into a technological one hit wonder.
Writing, though, will never be rendered obsolete by the fickle winds of technology or fashion.

**Writing is also a transferable skill.**
If you decide to change careers and become a
realtor,
landscape artist,
or -- God forbid! -- a manager,
your expertise in the Hoth MVVM framework will be of limited use.
But writing will remain.

## But what will I get out of studying writing?

### Less back and forth

We've all experienced out of control email threads.
It starts with a vague question,
is usually followed by an ambiguous answer,
and before you know it,
someone in another department is freaking out about a non-existent problem.

What if you could step in and make people understand?
Even better, what if you could prevent the issue from escalating in the first place?
That's the power of clear and effective writing.

### Fewer bugs and less rework

Sometimes we run across frustratingly mysterious bits of code.
Perhaps we've even written some ourselves.
But what's even more frustrating
is when the code is accompanied by an equally mysterious comment like,
"fixes IE bug."

What IE bug? And which version of IE?

A developer working on this code, now needs to:

* Talk to the original programmer,
  who won't remember anything.
* Manually test to see if there are obvious bugs related to that line,
  which he probably won't catch.
* And perhaps make changes anyway,
  without knowing what he broke.

A clearly written comment would have prevented all of that.

### Protect your development flow

Talking face to face is a valuable and necessary part of our jobs.
But not every face to face conversation is valuable.

If you find yourself explaining certain things over and over again
(perhaps because you're an expert in a particular subsystem)
then you should write it down in a public location like a team wiki.
If your explanations are clear and easy to understand,
you'll have fewer interruptions messing with your development flow.

### Recognition of your expertise

Perhaps you have a different problem.
You have a deep knowledge of some tool, library, or system,
but no one realizes it.
Writing guides, tutorials, and presentations
can help you achieve greater recognition,
both inside and outside your company.

### Greater trust from managers

Watching a software development project from the outside can be a frustrating experience.
This is doubly true if you are on the hook when something goes wrong.

A developer who can clearly articulate the state of the project,
without getting bogged down in technical details,
is invaluable.

If you are that developer,
you will quickly earn the trust of your manager and non-technical colleagues.

### Improve your own understanding

> "How can I tell you what I think till I see what I say?"
> 
> -- E.M. Forster, _Aspects of the Novel_

The process of writing takes our often fuzzy and unformed ideas,
and shapes them into something clear enough to communicate to others.
This makes writing an excellent way to deepen our own understanding of a subject.
Sometimes the improvement comes from research,
but often it comes simply due to organizing our own thoughts.

## What now?

Hopefully by this point you can see why studying writing is worthwhile.
In the next chapter,
we'll launch into some general rules you can use to improve your writing.

================================================
FILE: netlify.toml
================================================
[build]
  publish = "_site"
  command = "npm run build"
  #command = "npm run build && set -e && find ./_site -type f -name '*.html' -exec staticrypt -f password_template.html {} $PASSWORD -o {} \\;"
  functions = "functions"

# REDIRECT and HEADERS examples

# Redirect rule example
# For more information see:- https://www.netlify.com/docs/netlify-toml-reference/

#[[redirects]]
#  from = "/*"
#  to = "/blog/:splat"

# The default HTTP status code is 301, but you can define a different one e.g.
# status = 302

# Headers rule example
# For more information see:- https://www.netlify.com/docs/netlify-toml-reference/

#[[headers]]
#   Define which paths this specific [[headers]] block will cover.
#   for = "/*"

#[headers.values]
#   X-Frame-Options = "DENY"
#   X-XSS-Protection = "1; mode=block"
#   Content-Security-Policy = "frame-ancestors https://www.facebook.com"

# Redirects and headers are GLOBAL for all builds – they do not get scoped to
# contexts no matter where you define them in the file.
# For context-specific rules, use _headers or _redirects files, which are
# applied on a PER-DEPLOY basis.


================================================
FILE: outline.md
================================================
# Prose for Programmers Outline

Checked boxes indicate that the initial draft of a section is complete.

## [Why should developers learn to write?](manuscript/why.md)

- [x] Objections
    - [x] Paid to code, not write
        - [x] Laundry list of writing developers have to do
        - [x] Coding is writing
    - [x] I already know how to write. I did well in school.
        - [x] Literacy, even college level, doesn’t mean your writing is good
        - [x] Academics can even foster bad writing habits
    - [x] But I could be learning technology X instead
        - [x] Writing is a durable skill
        - [x] Writing is a transferable skill
- [x] Benefits
    - [x] fewer back and forth emails/chats
    - [x] Fewer bugs due to misunderstood code/docs
    - [x] Fewer interruptions from coworkers with questions (keep flow)
    - [x] More understanding/empathy from managers
    - [x] Recognition of your knowledge
    - [x] Increase your own understanding

## [General Rules](manuscript/rules.md)

- [x] Be goal oriented
    - [x] Everything you write has a goal or purpose
        - [x] may be implicit or explicit
    - [x] Understanding the goal promotes better writing
    - [x] Structure of goals
        - [x] 2 types
            - [x] inform
            - [x] persuade
        - [x] Most writing serves both to one degree or other
        - [x] But most writing has a primary goal, with others as secondary
- [x] Be concise
    - [x] Not short or terse
    - [x] Everything contributes to the goal
    - [x] Avoids wordiness for the sake of the goal
- [x] Be clear
    - [x] Avoid unfamiliar or unnecessary jargon
    - [x] Avoid ambiguity
    - [x] Be precise
- [x] Be organized
    - [x] Human mind uses patterns to extract meaning
    - [x] Structure is fractal: macro and micro mirror each other
    - [x] Projects may come with built-in structure (API docs)
    - [x] Several general purpose structures available
- [x] Be scannable
    - [x] Typographic structure to direct attention to ideas
    - [x] Break up text into digestible chunks
    - [x] use headlines
    - [x] use lists
    - [x] Make use of bold and italic to highlight points

## [The Writing Process](manuscript/process.md)

- [x] Determine the goal
    - [x] Why am I writing this?
        - [x] Purpose of doc
        - [x] Not personal motivations
    - [x] Keep asking why
- [x] Determine audience
    - [x] Who will be reading this?
    - [x] Given the goals, which readers are the primary audience?
    - [x] What is their relation to the subject?
    - [x] What is their relation to you?
    - [x] Examples of how audience affects writing
- [x] Choose a suitable structure
    - [x] Different structures are suitable for different goals
        - [x] Hierarchical structures are often good for informational
        - [x] Narrative is good for persuasive
    - [x] Different structures for different audiences
        - [x] Expertise in topic
            - [x] Shallow bullet points
            - [x] Deep information hierarchy
        - [x] Level of interest
        - [x] Relationship
    - [x] Can mix and match
        - [x] Examples
- [x] Build an outline
    - [x] Structure may come with an outline template (API docs)
    - [x] List of the ideas you want to communicate
    - [x] Evaluate outline based on goal and audience
    - [x] Revise outline
        - [x] Revising earlier is cheaper
- [x] Turn outline into prose
    - [x] Put flesh on the bones
        - [x] Muscles: human connection/emotion for strength
        - [x] Connective tissue: transitions between ideas
    - [x] Not necessarily exposition
        - [x] Stories
        - [x] Jokes
- [x] Revise
    - [x] Evaluate
        - [x] Does this unit advance the goal?
        - [x] Is it concise?
        - [x] Is it clear?
        - [x] Is it organized?
        - [x] Is it scannable?
    - [x] Plan a correction
    - [x] Make corrections
- [x] Get feedback
    - [x] Types of readers
        - [x] Target audience
        - [x] Experts
        - [x] Friends
    - [x] Evaluating feedback
        - [x] Readers are always right about their reactions
        - [x] Readers may be wrong about the problems and solutions they identify
        - [x] Bear in mind what kind of reader this is
    - [x] Produce a list of problems to address and good points to keep
- [x] Revise based on feedback
    - [x] See previous
- [x] Publish
    - [x] May be done at any point (depending on project)
    - [x] Can be part of an iterative process
    - [x] Tell people about it
    - [x] Not the end of revision
- [x] Conclusion
    - [x] Abbreviate process as appropriate

## Writing Structures

- [ ] General Structures
    - [ ] Inverted Pyramid
        - [ ] what it is
        - [ ] why it is useful (time saving)
        - [ ] high level
        - [ ] low level (topic sentences)
    - [ ] List
    - [ ] Sequence
    - [ ] Q & A ?
- [ ] Informational Structures
    - [ ] Hierarchy
        - [ ] Examples
            - [ ] Textbooks
            - [ ] API Docs
- [ ] Persuasive structures
    - [ ] Objection - response
    - [ ] Persuasive version of Q&A
    - [ ] Problem - solution
    - [ ] Story
    - [ ] Syllogism
    - [ ] Juxtaposition (attention grabbers)
- [ ] Combine as appropriate
    - [ ] Examples

## Audience

- [ ] Aspects of audience
    - [ ] Expertise in subject
    - [ ] Relationship to you
    - [ ] Motivation for reading
    - [ ] Emotional state
    - [ ] Level of caution/skepticism
- [ ] Common audiences for developers
    - [ ] Other developers
        - [ ] Somewhat similar outlook on the world
        - [ ] Not necessarily the same as you
            - [ ] Experience
            - [ ] Different technologies
            - [ ] Corporate vs startup
    - [ ] QA & testers
    - [ ] Designers
    - [ ] Teammates
    - [ ] Non-technical peers
    - [ ] Your boss
    - [ ] Management
    - [ ] Ops/SysAdmin
    - [ ] Users
    - [ ] Clients
    - [ ] Customers

## Genres

- [ ] Genres
- [ ] Email
- [ ] Docs
- [ ] Code Comments
- [ ] Blog posts
- [ ] Chat?
- [ ] Project site
- [ ] Case for new technology
- [ ] Requirements
- [ ] Bug reports
- [ ] Error messages
- [ ] Help articles
- [ ] UI text
- [ ] Commit messages
- [ ] Code review
- [ ] Code?
- [ ] Talks/presentations?

## Other Resources

- [ ] Books
    - [ ] The Elements of Style by Strunk & White
    - [ ] The Economist Style Guide
    - [ ] Steering the Craft by Ursula K. Le Guin
    - [ ] On Writing Well by William Zinsser
    - [ ] Writing That Works by Ogilvy?
    - [ ] Team Geek?
- [ ] Applications & Tools
    - [ ] Workflowy
    - [ ] Writeroom
    - [ ] Writemonkey
    - [ ] IA Writer
    - [ ] Hemingway?
    - [ ] write-good npm library?

================================================
FILE: package.json
================================================
{
  "name": "spacebook",
  "version": "1.0.0",
  "description": "A simple site generator based on Eleventy, Tailwind 2.0, and Alpine.js",
  "scripts": {
    "start": "eleventy --serve & postcss styles/tailwind.css --o _tmp/style.css --watch",
    "build": "ELEVENTY_PRODUCTION=true eleventy && NODE_ENV=production postcss styles/tailwind.css --o _site/style.css &&  ./node_modules/.bin/cleancss -o _site/style.css _site/style.css",
    "watch": "npx eleventy --watch",
    "debug": "DEBUG=* npx eleventy"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/broeker/spacebook.git"
  },
  "author": "Tim Broeker <broeker@gmail.com> (https://www.electriccitizen.com/)",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/broeker/spacebook/issues"
  },
  "homepage": "https://github.com/broeker/spacebook",
  "devDependencies": {
    "@11ty/eleventy": "^0.11.1",
    "alpinejs": "^2.7.3",
    "eleventy-plugin-lazyimages": "^2.1.0",
    "eslint": "^7.9.0",
    "lazysizes": "^5.2.2",
    "luxon": "^1.25.0",
    "markdown-it": "^10.0.0",
    "markdown-it-anchor": "^5.3.0",
    "markdown-it-image-lazysizes": "^1.0.0",
    "postcss-cli": "^8.3.0",
    "prettier": "^2.1.2",
    "tailwindcss": "^2.0.2"
  },
  "dependencies": {
    "@11ty/eleventy-img": "^0.5.0",
    "@11ty/eleventy-navigation": "^0.1.6",
    "@tailwindcss/forms": "^0.2.1",
    "@tailwindcss/typography": "^0.3.1",
    "autoprefixer": "^10.1.0",
    "clean-css": "^4.2.1",
    "clean-css-cli": "^4.3.0",
    "elasticlunr": "^0.9.5",
    "eleventy-plugin-embed-everything": "^1.9.4",
    "eleventy-plugin-nesting-toc": "^1.2.0",
    "eleventy-plugin-svg-contents": "^0.7.0",
    "eleventy-plugin-toc": "^1.1.0",
    "emoji-regex": "^9.2.0",
    "html-minifier": "^4.0.0",
    "markdown-it-attrs": "^3.0.3",
    "markdown-it-center-text": "^1.0.4",
    "markdown-it-container": "^3.0.0",
    "markdown-it-emoji": "^2.0.0",
    "markdown-it-footnote": "^3.0.2",
    "markdown-it-for-inline": "^0.1.1",
    "markdown-it-linkify-images": "^2.0.0",
    "markdown-it-table-of-contents": "^0.5.0",
    "markdown-it-task-lists": "^2.1.1",
    "postcss": "^8.2.2",
    "qs": "^6.9.4",
    "remove": "^0.1.5",
    "staticrypt": "^1.3.2",
    "uglify-es": "^3.3.9",
    "url-pattern": "^1.0.3"
  },
  "main": ".eleventy.js"
}


================================================
FILE: password_template.html
================================================
<!DOCTYPE html>
<html class="staticrypt-html">
  <head>
    <meta charset="utf-8" />
    <title>Private Page</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <!-- do not cache this page -->
    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />

    <style>
      .staticrypt-hr {
        margin-top: 20px;
        margin-bottom: 20px;
        border: 0;
        border-top: 1px solid #eee;
      }

      .staticrypt-page {
        width: 360px;
        padding: 8% 0 0;
        margin: auto;
        box-sizing: border-box;
      }

      .staticrypt-form {
        position: relative;
        z-index: 1;
        background: #ffffff;
        max-width: 360px;
        margin: 0 auto 100px;
        padding: 45px;
        text-align: center;
        box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2),
          0 5px 5px 0 rgba(0, 0, 0, 0.24);
      }

      .staticrypt-form input {
        outline: 0;
        background: #f2f2f2;
        width: 100%;
        border: 0;
        margin: 0 0 15px;
        padding: 15px;
        box-sizing: border-box;
        font-size: 14px;
      }

      .staticrypt-form .staticrypt-decrypt-button {
        text-transform: uppercase;
        outline: 0;
        background: white;
        width: 100%;
        border: 0;
        padding: 15px;
        color: black;
        font-size: 14px;
        cursor: pointer;
        border: 1px solid black;
      }

      .staticrypt-form .staticrypt-decrypt-button:hover {
        background: #E5E7EB;
      }

      .staticrypt-html {
        height: 100%;
      }

      .staticrypt-body {
        margin-bottom: 1em;
        background: white;
        font-family: "Arial", sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
      }

      .staticrypt-instructions {
        margin-top: -1em;
        margin-bottom: 1em;
      }

      .staticrypt-title {
        font-size: 1.5em;
      }

      .staticrypt-footer {
        position: fixed;
        height: 20px;
        font-size: 16px;
        padding: 2px;
        bottom: 0;
        left: 0;
        right: 0;
        margin-bottom: 0;
      }

      .staticrypt-footer p {
        margin: 2px;
        text-align: center;
        float: right;
      }

      .staticrypt-footer a {
        text-decoration: none;
      }

      #loader {
        border: 12px solid #f3f3f3;
        border-radius: 50%;
        border-top: 12px solid #444444;
        width: 70px;
        height: 70px;
        animation: spin 1s linear infinite;
      }

      @keyframes spin {
        100% {
          transform: rotate(360deg);
        }
      }

      .center {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
      }
    </style>
    <style>
      html {
        display: none !important;
        opacity: 0;
      }
    </style>
      <script src="_includes/assets/inline.js"></script>
  </head>

  <body class="staticrypt-body">
    <div id="loader" class="center"></div>
    {crypto_tag}

    <script>
       document.onreadystatechange = function () {
        if (document.readyState !== "complete") {
          document.querySelector("body").style.visibility = "hidden";
          document.querySelector("#loader").style.visibility = "visible";
        } else {
          document.querySelector("#loader").style.display = "none";
          document.querySelector("body").style.visibility = "visible";
        }
      };
      
      /**
       * Decrypt a salted msg using a password.
       * Inspired by https://github.com/adonespitogo
       */
      var keySize = 256;
      var iterations = 1000;
      function decrypt(encryptedMsg, pass) {
        var salt = CryptoJS.enc.Hex.parse(encryptedMsg.substr(0, 32));
        var iv = CryptoJS.enc.Hex.parse(encryptedMsg.substr(32, 32));
        var encrypted = encryptedMsg.substring(64);

        var key = CryptoJS.PBKDF2(pass, salt, {
          keySize: keySize / 32,
          iterations: iterations,
        });

        var decrypted = CryptoJS.AES.decrypt(encrypted, key, {
          iv: iv,
          padding: CryptoJS.pad.Pkcs7,
          mode: CryptoJS.mode.CBC,
        }).toString(CryptoJS.enc.Utf8);
        return decrypted;
      }

      window.onload = function () {
        var form = document.getElementById("staticrypt-form");
        if (form) {
          form.addEventListener("submit", function (e) {
            e.preventDefault();
            var passphrase = document.getElementById("staticrypt-password")
              .value;
            localStorage.setItem("passphrase", passphrase);
            if (!decryptWithPassphrase(passphrase)) {
              alert("Sorry, that password didn't work!");
              return;
            }
          });
        }
      };

      if (localStorage.getItem("passphrase")) {
        if (!decryptWithPassphrase(localStorage.getItem("passphrase"))) {
          localStorage.removeItem("passphrase");
        }
      }

      function decryptWithPassphrase(passphrase) {
        var encryptedMsg = "{encrypted}",
          encryptedHMAC = encryptedMsg.substring(0, 64),
          encryptedHTML = encryptedMsg.substring(64),
          decryptedHMAC = CryptoJS.HmacSHA256(
            encryptedHTML,
            CryptoJS.SHA256(passphrase).toString()
          ).toString();

        if (decryptedHMAC !== encryptedHMAC) {
          return false;
        }

        var plainHTML = decrypt(encryptedHTML, passphrase);
        var newHTML = document.open("text/html", "replace");
        newHTML.write(plainHTML);
        newHTML.close();

        return true;
      }
    </script>

    <div class="staticrypt-page">
      <div class="staticrypt-form">
        <div class="staticrypt-instructions">
          <p>🔒 Enter a password to unlock!</p>
        </div>

        <hr class="staticrypt-hr" />

        <form id="staticrypt-form" action="#" method="post">
          <input
            id="staticrypt-password"
            type="password"
            name="password"
            placeholder="password"
            autofocus
          />
          <input
            type="submit"
            class="staticrypt-decrypt-button"
            value="ENTER"
          />
        </form>
      </div>
    </div>
    <style>
      html {
        display: block !important;
        opacity: 1;
      }
    </style>
  </body>
</html>

================================================
FILE: postcss.config.js
================================================
module.exports = {
  plugins: [
    require(`tailwindcss`)(`./styles/tailwind.config.js`),
    require(`autoprefixer`),
  ],
};


================================================
FILE: robots.md
================================================
---
title: Robots 
permalink: /robots.txt
layout: layouts/robots.njk
---


================================================
FILE: search-index.json.njk
================================================
---
permalink: /search-index.json
---
{{ collections.results | search | dump | safe  }}




================================================
FILE: styles/tailwind.config.js
================================================
const autoprefixer = require('autoprefixer');

module.exports = {
  important: true,
  future: {
    removeDeprecatedGapUtilities: true,
    purgeLayersByDefault: true,
  },
  purge: {
    enabled: true,
    content: ["_site/**/*.html"],
    options: {
      safelist: [],
    },
  },
  darkMode: 'class',
  theme: {
    container: {
      center: true,
    },
    extend: {
      typography: {
        DEFAULT: {
          css: {
            maxWidth: '100%',
            a: {
              color: '#1D4ED8',
              '&:hover': {
              color: '#1E3A8A',
              },
            },
            '.prose a.edit, .tag a': {
              color: '#333',
              'text-decoration': 'none',
            },
            'ul.footer-nav': {
              '::before': {
                display: 'none',
                'text-decoration': 'none',
              }
            },
            'ul.contains-task-list': {
              '::before': {
                display: 'none',
              }
            },
            'ul.spacelog': {
              '::before': {
                display: 'none',
              }
            },
          },
        },
      }
    },
  }, 
  variants: {},
  plugins: [
    require('@tailwindcss/typography'),
    require('@tailwindcss/forms'),
  ],
}

================================================
FILE: styles/tailwind.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  
  /* Set up some default image behavior for nicer images */
  img {
    @apply w-auto  shadow-md border-2 border-transparent !important
  }
  img:hover {
    @apply border-2 border-gray-100
  }
  /* Overrides for Tailwind Typography prose class */
  .prose a {
    @apply dark:text-gray-400
  }
  .prose a:hover {
    @apply dark:text-gray-500
  }
  .prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 .prose hr, .prose strong {
    @apply dark:text-gray-400
  }
  .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
    scroll-margin-top: 3.5em;
  }
  .prose pre code {
    @apply overflow-x-auto !important 
  }
  .prose .footer-nav a {
    @apply no-underline !important
  }
  .prose ul.contains-task-list,
  .prose ul.spacelog  {
    @apply list-none -ml-6 !important;
  }
  .prose ul.contains-task-list .task-list-item,
  .prose ul.spacelog {
    ::before {
      @apply hidden !important;
    }
  }

  /* Define blockquotes and some standard callout blocks */
  blockquote {
    @apply rounded-lg p-4 bg-gray-100 dark:bg-gray-500 border-gray-200 border-l-8 dark:border-gray-700;
  }
  .callout {
    @apply px-8 py-4 mb-4 rounded-lg bg-yellow-50;
  }
  .callout, .callout strong, .callout em {
    @apply dark:bg-gray-400 dark:text-gray-900;
  }
  .callout-blue {
    @apply px-8 py-4 mb-4 rounded-lg bg-blue-50;
  }
  .callout-blue, .callout-blue strong, .callout-blue em {
    @apply dark:text-gray-200 dark:bg-blue-900;
  }
  .callout-pink {
    @apply px-8 py-4 mb-4 rounded-lg bg-pink-50;
  }
  .callout-pink, .callout-pink strong, .callout-pink em {
    @apply dark:text-gray-200 dark:bg-pink-900;
  }
  .callout-green {
    @apply px-8 py-4 mb-4 rounded-lg bg-green-50;
  }
  .callout-green, .callout-green strong, .callout-green em {
    @apply dark:text-gray-200 dark:bg-green-900;
  }
  .warning {
    @apply px-8 py-4 mb-4 rounded-lg bg-red-800 text-gray-50;
  }
  .warning, .warning strong, .warning em {
    @apply text-gray-50 dark:bg-red-900  dark:text-gray-200;
  }

  /* Overrides for nav/Table of Contents block */
  nav ul {
    @apply ml-0 text-gray-500;
   }
  nav ul ul {
   @apply ml-6 text-gray-500;
  }
  nav ul li a {
    @apply mb-1 pt-2 pr-4 pb-1 pl-2 w-full block text-gray-500 dark:text-gray-500;
  }
  nav ul li a:hover {
    @apply text-gray-900 dark:text-gray-400;
  }
  nav ul li a.active {
    @apply font-semibold;
  }
  nav.toc ol li {
    @apply pt-2 !important
  }
  nav.toc ol li li {
    @apply pt-2 ml-4
  }
  nav.toc ol li a {
    @apply text-gray-500
  }
  nav.toc ol li a:hover {
    @apply text-gray-900
  }
  .prose .footer-nav a:hover {
    @apply dark:text-gray-400 !important
  }

/* Utilities and misc */
  .adjust  p img, .adjust img, .adjust p iframe, .adjust iframe, .twitter-tweet {
    @apply shadow-md ml-auto mr-auto  p-2 !important
  }
  .adjust  p img:hover, .adjust img:hover {
    @apply shadow-xl  
  }
  .adjust img.button {
    @apply w-auto  shadow-none !important
  }
  .text-align-center {
    @apply flex justify-center; 
  }
  .icon-spacer {
    width: "24px";
  }
  input {
   @apply dark:text-gray-400
  }
}

blockquote p::before {
  content: '' !important;
}

blockquote p::after {
  content: '' !important;
}

blockquote>p:first-child {
  margin-top: 0;
}

blockquote>p:last-child {
  margin-bottom: 0;
}
Download .txt
gitextract_xvvrlvj7/

├── .eleventy.js
├── .eleventyignore
├── .forestry/
│   └── settings.yml
├── .gitattributes
├── .gitignore
├── .nvmrc
├── 404.md
├── LICENSE
├── LICENSE.txt
├── README.md
├── _data/
│   └── site.json
├── _includes/
│   ├── assets/
│   │   ├── css/
│   │   │   ├── 404.css
│   │   │   └── inline.css
│   │   ├── js/
│   │   │   ├── 404.js
│   │   │   ├── inline.js
│   │   │   └── search.js
│   │   └── utils/
│   │       ├── image.txt
│   │       └── imagesize.txt
│   ├── components/
│   │   ├── contact.njk
│   │   ├── footer.njk
│   │   ├── head.njk
│   │   ├── header.njk
│   │   └── nav.njk
│   ├── experimental/
│   │   └── blog/
│   │       ├── author.njk
│   │       ├── authors.njk
│   │       ├── components/
│   │       │   ├── pageslist.njk
│   │       │   └── postslist.njk
│   │       ├── content/
│   │       │   └── posts/
│   │       │       ├── firstpost.md
│   │       │       ├── fourthpost.md
│   │       │       ├── posts.json
│   │       │       ├── secondpost.md
│   │       │       ├── the-fifth-and-hopefully-final-example-post.md
│   │       │       └── thirdpost.md
│   │       ├── layouts/
│   │       │   ├── blog.njk
│   │       │   └── post.njk
│   │       ├── pages/
│   │       │   └── blog.md
│   │       └── tags.njk
│   └── layouts/
│       ├── 404.njk
│       ├── base.njk
│       ├── contact.njk
│       ├── page.njk
│       └── robots.njk
├── admin/
│   ├── config.yml
│   ├── index.html
│   └── preview-templates/
│       ├── index.js
│       ├── page.js
│       └── post.js
├── content/
│   ├── buy.md
│   └── pages/
│       ├── contact.md
│       ├── home.md
│       └── pages.json
├── filters/
│   └── searchFilter.js
├── loader.js
├── manuscript/
│   ├── Book.txt
│   ├── manuscript.json
│   ├── process.json
│   ├── process.md
│   ├── rules.json
│   ├── rules.md
│   ├── why.json
│   └── why.md
├── netlify.toml
├── outline.md
├── package.json
├── password_template.html
├── postcss.config.js
├── robots.md
├── search-index.json.njk
└── styles/
    ├── tailwind.config.js
    └── tailwind.css
Download .txt
SYMBOL INDEX (10 symbols across 5 files)

FILE: _includes/assets/js/404.js
  function drawVisor (line 19) | function drawVisor() {
  function animate (line 43) | function animate() {

FILE: _includes/assets/js/inline.js
  function logout (line 23) | function logout() {
  function showNavigation (line 28) | function showNavigation() {
  function closeNavigation (line 34) | function closeNavigation() {
  function activateDarkMode (line 40) | function activateDarkMode() {
  function toggleLayout (line 50) | function toggleLayout(state) {

FILE: admin/preview-templates/page.js
  method render (line 7) | render() {

FILE: admin/preview-templates/post.js
  method render (line 8) | render() {

FILE: filters/searchFilter.js
  function squash (line 21) | function squash(text) {
Condensed preview — 70 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (170K chars).
[
  {
    "path": ".eleventy.js",
    "chars": 8746,
    "preview": "const { DateTime } = require(\"luxon\");\nconst CleanCSS = require(\"clean-css\");\nconst UglifyJS = require(\"uglify-es\");\ncon"
  },
  {
    "path": ".eleventyignore",
    "chars": 42,
    "preview": "README.md\n.github/\nfunctions/*\nfunctions/\n"
  },
  {
    "path": ".forestry/settings.yml",
    "chars": 541,
    "preview": "---\nnew_page_extension: md\nauto_deploy: false\nadmin_path: ''\nwebhook_url: \nsections:\n- type: directory\n  path: ''\n  labe"
  },
  {
    "path": ".gitattributes",
    "chars": 436,
    "preview": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs     diff=csharp\n\n# St"
  },
  {
    "path": ".gitignore",
    "chars": 67,
    "preview": "_site/\nnode_modules/\npackage-lock.json\n.idea\n.vscode\n*~\n.cache\n_tmp"
  },
  {
    "path": ".nvmrc",
    "chars": 4,
    "preview": "v14\n"
  },
  {
    "path": "404.md",
    "chars": 64,
    "preview": "---\ntitle: 404\npermalink: /404.html\nlayout: layouts/404.njk\n---\n"
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2020 Tim Broeker \n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "LICENSE.txt",
    "chars": 17749,
    "preview": "Creative Commons\nAttribution-NonCommercial-NoDerivs 3.0 Unported\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND"
  },
  {
    "path": "README.md",
    "chars": 2151,
    "preview": "# Prose for Programmers\n\nThis is a book for programmers.\nIt is a guide to mastering the most difficult programming langu"
  },
  {
    "path": "_data/site.json",
    "chars": 1070,
    "preview": "{\n  \"name\": \"Prose for Programmers\",\n  \"subtitle\": \"Learn to write for people, not just computers\",\n  \"description\": \"Le"
  },
  {
    "path": "_includes/assets/css/404.css",
    "chars": 7235,
    "preview": "html,\nbody {\n    height: 100%;\n    width: 100%;\n    margin: 0px;\n    background: linear-gradient(90deg, #2f3640 23%, #18"
  },
  {
    "path": "_includes/assets/css/inline.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "_includes/assets/js/404.js",
    "chars": 1714,
    "preview": "(function (window, document) {\n    \"use strict\";\n    window.addEventListener('load', function () {\n        const cordCan"
  },
  {
    "path": "_includes/assets/js/inline.js",
    "chars": 1924,
    "preview": "if (window.netlifyIdentity) {\n  window.netlifyIdentity.on(\"init\", user => {\n    if (!user) {\n      window.netlifyIdentit"
  },
  {
    "path": "_includes/assets/js/search.js",
    "chars": 2131,
    "preview": "(function (window, document) {\n    \"use strict\";\n    const search = (e) => {\n      const results = window.searchIndex.se"
  },
  {
    "path": "_includes/assets/utils/image.txt",
    "chars": 42,
    "preview": "{% image \"sagan.jpg\" \"Carl Sagan, 1987\" %}"
  },
  {
    "path": "_includes/assets/utils/imagesize.txt",
    "chars": 50,
    "preview": "{% image \"sagan.jpg\" \"Carl Sagan, 1987\" \"200px\" %}"
  },
  {
    "path": "_includes/components/contact.njk",
    "chars": 1676,
    "preview": "<div class=\"flex w-full\">\n  <div class=\"w-full bg-white dark:bg-gray-700 rounded shadow-lg p-8 md:max-w-lg md:mx-auto\">\n"
  },
  {
    "path": "_includes/components/footer.njk",
    "chars": 4339,
    "preview": "<div class=\"z-50 mt-12 h-12 flex dark:bg-gray-900 text-gray-500 dark:text-gray-400 justify-center p-2 border-t border-gr"
  },
  {
    "path": "_includes/components/head.njk",
    "chars": 1258,
    "preview": "<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>{{ ren"
  },
  {
    "path": "_includes/components/header.njk",
    "chars": 3991,
    "preview": "<div class=\"fixed w-full top-0 left-0 z-10 \"> \n    <nav class=\"flex h-16 md:h-20 items-center bg-white dark:bg-gray-900 "
  },
  {
    "path": "_includes/components/nav.njk",
    "chars": 4894,
    "preview": "{% set navPages = collections.all | eleventyNavigation  %}\n\n{% macro renderNavListItem(entry,rel) -%}\n\n  {% if entry.url"
  },
  {
    "path": "_includes/experimental/blog/author.njk",
    "chars": 1321,
    "preview": "---\ntitle: Author archive\nlayout: layouts/base.njk\npagination:\n  data: collections.authors\n  size: 1\n  alias: author\nper"
  },
  {
    "path": "_includes/experimental/blog/authors.njk",
    "chars": 486,
    "preview": "---\ntitle: Authors\nmetaDescription: This page lists all of the Blog's authors.\nlayout: layouts/base.njk\npagination:\n  da"
  },
  {
    "path": "_includes/experimental/blog/components/pageslist.njk",
    "chars": 917,
    "preview": "<section>\n\t{% for page in pageslist | reverse %}\n\t\t<article{% if post.url == url %} data-current=\"current item\"{% endif "
  },
  {
    "path": "_includes/experimental/blog/components/postslist.njk",
    "chars": 1143,
    "preview": "<section>\n<ul class=\"spacelog\">\n\t{% for post in postslist | reverse %}\n\t<li>\n\t\t<time datetime=\"{{ post.date | machineDat"
  },
  {
    "path": "_includes/experimental/blog/content/posts/firstpost.md",
    "chars": 1506,
    "preview": "---\ntitle: This is the first example post\nmetaDescription: This is a sample meta description. If one is not present in y"
  },
  {
    "path": "_includes/experimental/blog/content/posts/fourthpost.md",
    "chars": 677,
    "preview": "---\ntitle: This is the fourth example post\ndate: 2020-02-03\nauthor: John Doe\nsummary: Why contemplating our mortality ca"
  },
  {
    "path": "_includes/experimental/blog/content/posts/posts.json",
    "chars": 142,
    "preview": "{\n  \"layout\": \"layouts/post.njk\",\n  \"permalink\": \"posts/{{ title | slug }}/index.html\",\n  \"author\": \"Anonymous\",\n  \"tags"
  },
  {
    "path": "_includes/experimental/blog/content/posts/secondpost.md",
    "chars": 1092,
    "preview": "---\ntitle: This is the second example post\nsummary: Why contemplating our mortality can be a powerful catalyst for chang"
  },
  {
    "path": "_includes/experimental/blog/content/posts/the-fifth-and-hopefully-final-example-post.md",
    "chars": 935,
    "preview": "---\ntitle: The fifth and hopefully final example post yo yo\ndate: 2020-10-15T12:23:39.598Z\nauthor: Jane Doe\nsummary: Why"
  },
  {
    "path": "_includes/experimental/blog/content/posts/thirdpost.md",
    "chars": 1189,
    "preview": "---\ntitle: This is the third example post which has a slightly longer title than the others\ndate: 2020-01-01\nauthor: Jan"
  },
  {
    "path": "_includes/experimental/blog/layouts/blog.njk",
    "chars": 597,
    "preview": "---\nlayout: layouts/base.njk\nsection: blog\npermalink: /blog/index.html\n---\n<div class=\"flex w-full justify-center\">\n    "
  },
  {
    "path": "_includes/experimental/blog/layouts/post.njk",
    "chars": 3486,
    "preview": "---\nlayout: layouts/base.njk\nsection: post\n---\n{% if site.navigationStyle == \"vertical\" %}\n    {% if site.enableEditButt"
  },
  {
    "path": "_includes/experimental/blog/pages/blog.md",
    "chars": 143,
    "preview": "---\nlayout: layouts/blog.njk\ntitle: Spacelog \ndate: 2020-12-21\npermalink: /blog/index.html\neleventyNavigation:\n  key: Sp"
  },
  {
    "path": "_includes/experimental/blog/tags.njk",
    "chars": 779,
    "preview": "---\npagination:\n  data: collections\n  size: 1\n  alias: tag\n  filter:\n    - all\n    - nav\n    - post\n    - posts\n    - pa"
  },
  {
    "path": "_includes/layouts/404.njk",
    "chars": 2058,
    "preview": "<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scal"
  },
  {
    "path": "_includes/layouts/base.njk",
    "chars": 1852,
    "preview": "<!doctype html>\n<html {% if section %} data-current=\"{{ section }}\"{% endif %} lang=\"en\">\n  {% include \"components/head."
  },
  {
    "path": "_includes/layouts/contact.njk",
    "chars": 172,
    "preview": "---\nlayout: layouts/base.njk\nsection: contact\n---\n<div class=\"prose prose-sm sm:prose lg:prose-lg xl:prose-xl mt-8 pl-4 "
  },
  {
    "path": "_includes/layouts/page.njk",
    "chars": 4801,
    "preview": "---\nlayout: layouts/base.njk\nsection: page\n---\n\n{% if site.navigationStyle == \"vertical\" %}\n    {% if site.enableEditBut"
  },
  {
    "path": "_includes/layouts/robots.njk",
    "chars": 25,
    "preview": "User-agent: * Disallow: /"
  },
  {
    "path": "admin/config.yml",
    "chars": 4057,
    "preview": "backend:\n  name: git-gateway\n  branch: main # Branch to update (optional; defaults to main)\n\n# Uncomment below to enable"
  },
  {
    "path": "admin/index.html",
    "chars": 587,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, init"
  },
  {
    "path": "admin/preview-templates/index.js",
    "chars": 734,
    "preview": "import Page from \"/admin/preview-templates/page.js\";\n// import Post from \"/admin/preview-templates/post.js\";\n\n// Registe"
  },
  {
    "path": "admin/preview-templates/page.js",
    "chars": 425,
    "preview": "import htm from \"https://unpkg.com/htm?module\";\n\nconst html = htm.bind(h);\n\n// Preview component for a Page\nconst Page ="
  },
  {
    "path": "admin/preview-templates/post.js",
    "chars": 1117,
    "preview": "import htm from \"https://unpkg.com/htm?module\";\nimport format from \"https://unpkg.com/date-fns@2.7.0/esm/format/index.js"
  },
  {
    "path": "content/buy.md",
    "chars": 132,
    "preview": "---\npermalink: false\neleventyNavigation:\n  key: Get the eBook edition\n  order: 10\n  url: https://leanpub.com/proseforpro"
  },
  {
    "path": "content/pages/contact.md",
    "chars": 479,
    "preview": "---\nlayout: layouts/contact.njk\ntitle: Send a message \nsection: contact\ndate: Last Modified\npermalink: /contact/index.ht"
  },
  {
    "path": "content/pages/home.md",
    "chars": 1461,
    "preview": "---\ntitle: About this book\npermalink: /\neleventyNavigation:\n  key: About this book\n  order: 0\n---\n\n_Prose for Programmer"
  },
  {
    "path": "content/pages/pages.json",
    "chars": 35,
    "preview": "{\n  \"layout\": \"layouts/page.njk\"\n}\n"
  },
  {
    "path": "filters/searchFilter.js",
    "chars": 2026,
    "preview": "const elasticlunr = require(\"elasticlunr\");\nconst emojiRegex = require('emoji-regex/RGI_Emoji.js')\n\nmodule.exports = fun"
  },
  {
    "path": "loader.js",
    "chars": 313,
    "preview": "if (localStorage.getItem('password')) {\n    insertPlainHTML(atob(localStorage.getItem('password')))\n} else {\n    documen"
  },
  {
    "path": "manuscript/Book.txt",
    "chars": 26,
    "preview": "why.md\nrules.md\nprocess.md"
  },
  {
    "path": "manuscript/manuscript.json",
    "chars": 35,
    "preview": "{\n  \"layout\": \"layouts/page.njk\"\n}\n"
  },
  {
    "path": "manuscript/process.json",
    "chars": 116,
    "preview": "{\n  \"title\": \"The Writing Process\",\n  \"eleventyNavigation\": {\n    \"key\": \"The Writing Process\",\n    \"order\": 3\n  }\n}"
  },
  {
    "path": "manuscript/process.md",
    "chars": 24180,
    "preview": "# The Writing Process\n\nMany people are under the impression that writing is a mystical\n-- or at least mysterious --\nacti"
  },
  {
    "path": "manuscript/rules.json",
    "chars": 104,
    "preview": "{\n  \"title\": \"General Rules\",\n  \"eleventyNavigation\": {\n    \"key\": \"General Rules\",\n    \"order\": 2\n  }\n}"
  },
  {
    "path": "manuscript/rules.md",
    "chars": 11521,
    "preview": "# General Rules\n\nRules.\nThe very word can trigger skepticism.\nAnd understandably so.\nWe are often confronted with seemin"
  },
  {
    "path": "manuscript/why.json",
    "chars": 150,
    "preview": "{\n  \"title\": \"Why should developers study writing?\",\n  \"eleventyNavigation\": {\n    \"key\": \"Why should developers study w"
  },
  {
    "path": "manuscript/why.md",
    "chars": 5588,
    "preview": "# Why should developers study writing?\n\nIf you've picked up this book, then you're probably a software developer.\nFurthe"
  },
  {
    "path": "netlify.toml",
    "chars": 1119,
    "preview": "[build]\n  publish = \"_site\"\n  command = \"npm run build\"\n  #command = \"npm run build && set -e && find ./_site -type f -n"
  },
  {
    "path": "outline.md",
    "chars": 6712,
    "preview": "# Prose for Programmers Outline\n\nChecked boxes indicate that the initial draft of a section is complete.\n\n## [Why should"
  },
  {
    "path": "package.json",
    "chars": 2315,
    "preview": "{\n  \"name\": \"spacebook\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A simple site generator based on Eleventy, Tailwind 2.0"
  },
  {
    "path": "password_template.html",
    "chars": 6669,
    "preview": "<!DOCTYPE html>\n<html class=\"staticrypt-html\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Private Page</title>\n   "
  },
  {
    "path": "postcss.config.js",
    "chars": 128,
    "preview": "module.exports = {\n  plugins: [\n    require(`tailwindcss`)(`./styles/tailwind.config.js`),\n    require(`autoprefixer`),\n"
  },
  {
    "path": "robots.md",
    "chars": 73,
    "preview": "---\ntitle: Robots \npermalink: /robots.txt\nlayout: layouts/robots.njk\n---\n"
  },
  {
    "path": "search-index.json.njk",
    "chars": 90,
    "preview": "---\npermalink: /search-index.json\n---\n{{ collections.results | search | dump | safe  }}\n\n\n"
  },
  {
    "path": "styles/tailwind.config.js",
    "chars": 1298,
    "preview": "const autoprefixer = require('autoprefixer');\n\nmodule.exports = {\n  important: true,\n  future: {\n    removeDeprecatedGap"
  },
  {
    "path": "styles/tailwind.css",
    "chars": 3378,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  \n  /* Set up some default image behavior for"
  }
]

About this extraction

This page contains the full source code of the joshuacc/prose-for-programmers GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 70 files (155.6 KB), approximately 42.8k tokens, and a symbol index with 10 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!