├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .tool-versions ├── .yarn └── releases │ └── yarn-4.0.2.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── components ├── SyntaxHighlighter.tsx └── Template.tsx ├── dev ├── global.css ├── lighthouserc.js ├── netlify.toml ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── _app.tsx ├── _document.tsx └── index.tsx ├── postcss.config.js ├── public └── images │ ├── favicon.ico │ └── open-graph-logo.png ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 4 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Handle line endings in Git 2 | * text=auto 3 | *.ico binary 4 | *.png binary 5 | 6 | # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored 7 | /.yarn/releases/** binary 8 | /.yarn/plugins/** binary 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version-file: ".tool-versions" 18 | - name: Get the Yarn cache directory path 19 | id: yarn-cache-dir-path 20 | run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT 21 | - uses: actions/cache@v4 22 | with: 23 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 24 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 25 | restore-keys: | 26 | ${{ runner.os }}-yarn- 27 | - name: Install packages 28 | run: yarn install --immutable 29 | - name: Check formatting 30 | run: yarn run format:check 31 | # - name: Lint the code 32 | # run: yarn run lint 33 | - name: Build the website 34 | run: yarn run build 35 | - name: Run Lighthouse 36 | run: npx --package=@lhci/cli lhci autorun 37 | env: 38 | LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | .DS_Store 4 | .next/ 5 | build/ 6 | node_modules/ 7 | 8 | # Yarn 9 | # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored 10 | .pnp.* 11 | .yarn/* 12 | !.yarn/patches 13 | !.yarn/plugins 14 | !.yarn/releases 15 | !.yarn/sdks 16 | !.yarn/versions 17 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.11.1 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md 2 | .next/ 3 | .yarn/ 4 | build/ 5 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 20.11.1 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | defaultSemverRangePrefix: "" 4 | 5 | enableGlobalCache: false 6 | 7 | nodeLinker: node-modules 8 | 9 | yarnPath: .yarn/releases/yarn-4.0.2.cjs 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Danny Guo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Make a README 2 | 3 | [![Netlify Status](https://api.netlify.com/api/v1/badges/68992d1c-36d4-4a84-b177-00c1f64fbcb4/deploy-status)](https://app.netlify.com/sites/make-a-readme/deploys) 4 | [![CI status](https://github.com/dguo/make-a-readme/workflows/CI/badge.svg)](https://github.com/dguo/make-a-readme/actions?query=branch%3Amain) 5 | 6 | [Make a README](https://makeareadme.com) explains what a README is, what the 7 | benefits are, and what makes for a good README. It also provides an editable 8 | template, with live Markdown rendering. 9 | 10 | I hope people who are new to programming will find it useful, but even for 11 | more experienced programmers, I think it's worth evaluating once 12 | in a while if we are doing a good job when it comes to the 13 | [small details](https://chris.beams.io/posts/git-commit/) that 14 | can matter more than we might think. 15 | 16 | Make a README is inspired by [Keep a Changelog](http://keepachangelog.com/). 17 | 18 | ## Related Resources 19 | 20 | - [Art of README](https://github.com/noffle/art-of-readme) 21 | - [Awesome README](https://github.com/matiassingers/awesome-readme) 22 | - [Standard Readme](https://github.com/RichardLitt/standard-readme) 23 | 24 | ## Roadmap 25 | 26 | - Translate the website 27 | - Create an interactive README generator ([#15](https://github.com/dguo/make-a-readme/issues/15)) 28 | - Link to great examples ([#14](https://github.com/dguo/make-a-readme/issues/14)) 29 | - check and improve accessibility 30 | 31 | ## Contributing 32 | 33 | Please feel free to submit an issue or pull request. To develop, you'll need 34 | Node.js. Run `yarn install && yarn dev`. 35 | 36 | ## License 37 | 38 | [MIT](https://github.com/dguo/make-a-readme/blob/main/LICENSE) 39 | -------------------------------------------------------------------------------- /components/SyntaxHighlighter.tsx: -------------------------------------------------------------------------------- 1 | import {LightAsync} from "react-syntax-highlighter"; 2 | import {a11yDark} from "react-syntax-highlighter/dist/cjs/styles/hljs"; 3 | 4 | function SyntaxHighlighter(props) { 5 | const {children, className} = props; 6 | if (!children) { 7 | return null; 8 | } 9 | 10 | // An example className is "language-python" 11 | const languageMatch = /language-(\w+)/.exec(className || ""); 12 | 13 | /* This is a simplified version of the example from the react-markdown docs: 14 | https://github.com/remarkjs/react-markdown?tab=readme-ov-file#use-custom-components-syntax-highlight 15 | 16 | If we have a language, we render it as a code block with syntax 17 | highlighting. Otherwise, we render an inline code element. 18 | 19 | One issue is that this approach doesn't handle a code block that doesn't 20 | have a language identifier, though GitHub supports that: 21 | https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks 22 | 23 | It is rendered as an inline code element instead of a code block. See 24 | this issue for more context: 25 | https://github.com/remarkjs/react-markdown/issues/776 26 | 27 | If we could distinguish between inline code and code blocks, we could 28 | still use the syntax highlighter and pass in plain text for the language. 29 | Instead, our hacky fix for this is to apply CSS by targeting the 30 | language-less code blocks and copying over the styling that is provided 31 | by github-markdown-css. See the ".markdown-body pre > code" selector in 32 | global.css. 33 | */ 34 | return languageMatch ? ( 35 | 41 | ) : ( 42 | 43 | ); 44 | } 45 | 46 | export default SyntaxHighlighter; 47 | -------------------------------------------------------------------------------- /components/Template.tsx: -------------------------------------------------------------------------------- 1 | import {useState} from "react"; 2 | import ReactMarkdown from "react-markdown"; 3 | import TextareaAutosize from "react-textarea-autosize"; 4 | import SyntaxHighlighter from "./SyntaxHighlighter"; 5 | 6 | const INITIAL_TEMPLATE = `# Foobar 7 | 8 | Foobar is a Python library for dealing with word pluralization. 9 | 10 | ## Installation 11 | 12 | Use the package manager [pip](https://pip.pypa.io/en/stable/) to install foobar. 13 | 14 | \`\`\`bash 15 | pip install foobar 16 | \`\`\` 17 | 18 | ## Usage 19 | 20 | \`\`\`python 21 | import foobar 22 | 23 | # returns 'words' 24 | foobar.pluralize('word') 25 | 26 | # returns 'geese' 27 | foobar.pluralize('goose') 28 | 29 | # returns 'phenomenon' 30 | foobar.singularize('phenomena') 31 | \`\`\` 32 | 33 | ## Contributing 34 | 35 | Pull requests are welcome. For major changes, please open an issue first 36 | to discuss what you would like to change. 37 | 38 | Please make sure to update tests as appropriate. 39 | 40 | ## License 41 | 42 | [MIT](https://choosealicense.com/licenses/mit/)`; 43 | 44 | export function Template() { 45 | const [template, setTemplate] = useState(INITIAL_TEMPLATE); 46 | 47 | return ( 48 |
49 |

Template

50 | 51 |
52 |
53 |

54 | 57 |

58 | { 63 | setTemplate(event.target.value); 64 | }} 65 | /> 66 |
67 |
68 |

69 | Rendered 70 |

71 |
75 | 76 | {template} 77 | 78 |
79 |
80 |
81 |
82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # ./dev --help 3 | 4 | import argparse 5 | from http.client import HTTPException 6 | import os 7 | from subprocess import call 8 | import sys 9 | from threading import Thread 10 | import time 11 | from urllib.error import URLError 12 | from urllib.request import urlopen 13 | import webbrowser 14 | 15 | parser = argparse.ArgumentParser(prog='./dev') 16 | subparsers = parser.add_subparsers(metavar='', title='commands') 17 | 18 | def open_in_browser(): 19 | site = 'http://localhost:8080' 20 | while True: 21 | try: 22 | urlopen(site) 23 | webbrowser.open(site) 24 | return 25 | except (ConnectionError, URLError, HTTPException): 26 | time.sleep(1) 27 | 28 | def run(port=False): 29 | command = ['docker', 'run', '--init', '-it', '--rm', '-v', 30 | os.getcwd() + ':/src:cached', '-w=/src'] 31 | if port: 32 | command += ['-p', '127.0.0.1:8080:8080'] 33 | 34 | command.append('node:18.16.0-alpine') 35 | 36 | return command 37 | 38 | def format(args, remaining): 39 | call(run() + ['yarn', 'format']) 40 | parser_format = subparsers.add_parser('format', 41 | help='format the code') 42 | parser_format.set_defaults(func=format) 43 | 44 | def lint(args, remaining): 45 | call(run() + ['yarn', 'lint']) 46 | parser_lint = subparsers.add_parser('lint', 47 | help='lint the code') 48 | parser_lint.set_defaults(func=lint) 49 | 50 | def start(args, remaining): 51 | Thread(target=open_in_browser).start() 52 | call(run(True) + ['yarn', 'start']) 53 | parser_start = subparsers.add_parser('start', 54 | help='start a development environment') 55 | parser_start.set_defaults(func=start) 56 | 57 | def sh(args, remaining): 58 | call(run() + ['sh']) 59 | parser_sh = subparsers.add_parser('sh', help='bring up a shell') 60 | parser_sh.set_defaults(func=sh) 61 | 62 | def yarn(args, remaining): 63 | call(run() + ['yarn'] + remaining or []) 64 | parser_yarn = subparsers.add_parser('yarn', help='run a yarn command') 65 | parser_yarn.set_defaults(func=yarn) 66 | 67 | if len(sys.argv) > 1: 68 | args, remaining = parser.parse_known_args() 69 | args.func(args, remaining) 70 | else: 71 | parser.print_help() 72 | -------------------------------------------------------------------------------- /global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | html { 7 | @apply font-sans text-gray-700; 8 | } 9 | 10 | h1 { 11 | @apply text-4xl font-bold; 12 | } 13 | 14 | *:not(#rendered) > h2 { 15 | @apply text-3xl font-bold mb-6 text-center; 16 | } 17 | 18 | h3 { 19 | @apply text-2xl font-bold mb-4; 20 | } 21 | 22 | p { 23 | @apply mt-3; 24 | } 25 | 26 | #rendered ul { 27 | @apply list-disc; 28 | } 29 | 30 | #rendered ol { 31 | @apply list-decimal; 32 | } 33 | 34 | code { 35 | @apply bg-gray-100 p-1 rounded-md; 36 | } 37 | 38 | a { 39 | @apply text-blue-600; 40 | } 41 | 42 | a:hover { 43 | @apply underline; 44 | } 45 | } 46 | 47 | a.anchorjs-link:hover { 48 | @apply no-underline; 49 | } 50 | 51 | #template h2 > a.anchorjs-link, 52 | #faq h2 > a.anchorjs-link { 53 | @apply text-blue-400; 54 | } 55 | 56 | /* For code examples in the rendered template, avoid padding because the div 57 | that comes with react-syntax-highlighter has padding. */ 58 | .markdown-body pre { 59 | padding: 0px; 60 | } 61 | 62 | /* This is a hacky fix for an issue with syntax highlighting. See the comment in 63 | SyntaxHighlighter.tsx. */ 64 | .markdown-body pre > code { 65 | display: block; 66 | overflow-x: auto; 67 | background: rgb(43, 43, 43); 68 | color: rgb(248, 248, 242); 69 | padding: 0.5em; 70 | } 71 | -------------------------------------------------------------------------------- /lighthouserc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ci: { 3 | collect: { 4 | staticDistDir: "build" 5 | }, 6 | upload: { 7 | target: "temporary-public-storage" 8 | } 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "yarn run build" 3 | publish = "build" 4 | 5 | # Plausible 6 | [[redirects]] 7 | from = "/lava-cake/js/script.js" 8 | to = "https://plausible.io/js/script.js" 9 | status = 200 10 | [[redirects]] 11 | from = "/lava-cake/api/event" 12 | to = "https://plausible.io/api/event" 13 | status = 200 14 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** 4 | * @type {import('next').NextConfig} 5 | **/ 6 | const nextConfig = { 7 | distDir: "build", 8 | output: "export", 9 | productionBrowserSourceMaps: true 10 | }; 11 | 12 | module.exports = nextConfig; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "make-a-readme", 3 | "description": "Website for explaining what a README is", 4 | "version": "0.1.0", 5 | "scripts": { 6 | "dev": "yarn install && next dev", 7 | "build": "next build", 8 | "format": "prettier --write .", 9 | "format:check": "prettier --check ." 10 | }, 11 | "repository": "https://github.com/dguo/make-a-readme", 12 | "author": "Danny Guo", 13 | "license": "MIT", 14 | "private": true, 15 | "dependencies": { 16 | "@fortawesome/fontawesome-svg-core": "6.5.1", 17 | "@fortawesome/free-solid-svg-icons": "6.5.1", 18 | "@fortawesome/react-fontawesome": "0.2.0", 19 | "github-markdown-css": "5.5.1", 20 | "react": "18.2.0", 21 | "react-dom": "18.2.0", 22 | "react-github-corner": "2.5.0", 23 | "react-markdown": "9.0.1", 24 | "react-syntax-highlighter": "15.5.0", 25 | "react-textarea-autosize": "8.5.3" 26 | }, 27 | "devDependencies": { 28 | "@types/node": "20.11.20", 29 | "@types/react": "18.2.57", 30 | "@types/react-syntax-highlighter": "15.5.11", 31 | "autoprefixer": "10.4.17", 32 | "next": "14.0.0", 33 | "postcss": "8.4.35", 34 | "prettier": "3.2.5", 35 | "tailwindcss": "3.4.1", 36 | "typescript": "5.3.3" 37 | }, 38 | "prettier": { 39 | "bracketSpacing": false, 40 | "tabWidth": 4, 41 | "trailingComma": "none" 42 | }, 43 | "packageManager": "yarn@4.0.2" 44 | } 45 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "github-markdown-css/github-markdown-light.css"; 2 | import "../global.css"; 3 | 4 | import "@fortawesome/fontawesome-svg-core/styles.css"; 5 | import {config} from "@fortawesome/fontawesome-svg-core"; 6 | // Skip adding the CSS automatically since it's being imported above 7 | config.autoAddCss = false; 8 | 9 | export default function App({Component, pageProps}) { 10 | return ; 11 | } 12 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, {Html, Head, Main, NextScript} from "next/document"; 2 | 3 | class MyDocument extends Document { 4 | render() { 5 | return ( 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | 17 | export default MyDocument; 18 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import Script from "next/script"; 3 | import GitHubCorner from "react-github-corner"; 4 | import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; 5 | import {faCircle, faFileAlt} from "@fortawesome/free-solid-svg-icons"; 6 | import {PropsWithChildren} from "react"; 7 | 8 | import {Template} from "../components/Template"; 9 | 10 | type SectionProps = PropsWithChildren<{ 11 | heading: string; 12 | }>; 13 | 14 | function Section(props: SectionProps) { 15 | return ( 16 |
17 |
18 |

{props.heading}

19 | {props.children} 20 |
21 |
22 | ); 23 | } 24 | 25 | type SectionItemProps = PropsWithChildren<{ 26 | heading: string; 27 | isFAQ?: boolean; 28 | }>; 29 | 30 | function SectionItem(props: SectionItemProps) { 31 | return ( 32 |
33 |

40 | {props.heading} 41 |

42 | {props.isFAQ ? ( 43 |
44 | {props.children} 45 |
46 | ) : ( 47 | props.children 48 | )} 49 |
50 | ); 51 | } 52 | 53 | export default function Home() { 54 | return ( 55 | <> 56 | 57 | 58 | 62 | 63 | Make a README 64 | 65 | {/* Search Engine */} 66 | 70 | {/* Schema.org for Google */} 71 | 72 | 76 | {/* Twitter */} 77 | 78 | 79 | 83 | 84 | {/* Open Graph general (Facebook, Pinterest & Google+) */} 85 | 86 | 90 | 91 | 92 | 93 | 97 | 98 | 99 | 100 | 101 | 106 | 107 | 108 | 113 | 114 |
115 | 116 | 117 | 122 | 123 |

Make a README

124 |

125 | Because no one can read your mind ( 126 | 127 | yet 128 | 129 | ) 130 |

131 |
132 | 133 | 140 | 141 |
142 | 143 |

144 | A{" "} 145 | 146 | README 147 | {" "} 148 | is a text file that introduces and explains a project. 149 | It contains information that is commonly required to 150 | understand what the project is about. 151 |

152 |
153 | 154 | 155 |

156 | It's an easy way to answer questions that your audience 157 | will likely have regarding how to install and use your 158 | project and also how to collaborate with you. 159 |

160 |
161 | 162 | 163 |

164 | Anyone who is working on a programming project, 165 | especially if you want others to use it or contribute. 166 |

167 |
168 | 169 | 170 |

171 | Definitely before you show a project to other people or 172 | make it public. You might want to get into the habit of 173 | making it the{" "} 174 | 175 | first file you create 176 | {" "} 177 | in a new project. 178 |

179 |
180 | 181 | 182 |

183 | In the top level directory of the project. This is where 184 | someone who is new to your project will start out. Code 185 | hosting services such as{" "} 186 | GitHub,{" "} 187 | Bitbucket, and{" "} 188 | GitLab will also 189 | look for your README and display it along with the list 190 | of files and directories in your project. 191 |

192 |
193 | 194 | 195 |

196 | While READMEs can be written in any text file format, 197 | the most common one that is used nowadays is{" "} 198 | 199 | Markdown 200 | 201 | . It allows you to add some lightweight formatting. You 202 | can learn more about it at the{" "} 203 | CommonMark website 204 | , which also has a helpful{" "} 205 | 206 | reference guide 207 | {" "} 208 | and an{" "} 209 | 210 | interactive tutorial 211 | 212 | . 213 |

214 |

215 | Some other formats that you might see are{" "} 216 | 217 | plain text 218 | 219 | ,{" "} 220 | 221 | reStructuredText 222 | {" "} 223 | (common in Python{" "} 224 | projects), and{" "} 225 | 226 | Textile 227 | 228 | . 229 |

230 |

231 | You can use any text editor. There are plugins for many 232 | editors (e.g.{" "} 233 | 234 | Atom 235 | 236 | ,{" "} 237 | 238 | Emacs 239 | 240 | ,{" "} 241 | 242 | Sublime Text 243 | 244 | ,{" "} 245 | 246 | Vim 247 | 248 | , and{" "} 249 | 250 | Visual Studio Code 251 | 252 | ) that allow you to preview Markdown while you are 253 | editing it. 254 |

255 |

256 | You can also use a dedicated Markdown editor like{" "} 257 | Typora or an online one 258 | like StackEdit or{" "} 259 | Dillinger. You can 260 | even use the editable template below. 261 |

262 |
263 |
264 | 265 |