├── .prettierrc ├── src ├── js │ ├── main.js │ ├── components │ │ └── Example.js │ └── App.js ├── css │ ├── global.css │ └── reset.css └── index.html ├── .babelrc ├── .eslintrc ├── static-assets.js ├── webpack.config.js ├── LICENCE ├── .gitignore ├── package.json └── README.md /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | quoteProps: "consistent", 3 | printWidth: 110, 4 | tabWidth: 2, 5 | singleQuote: true, 6 | bracketSpacing: false 7 | } 8 | -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {render} from 'react-dom'; 3 | import App from './App.js'; 4 | 5 | render(, document.querySelector('[data-app="main"]')); 6 | -------------------------------------------------------------------------------- /src/css/global.css: -------------------------------------------------------------------------------- 1 | @import url('./reset.css'); 2 | 3 | body { 4 | padding: 2rem; 5 | background: #efefef; 6 | color: #252525; 7 | font-family: sans-serif; 8 | } 9 | 10 | a:not([class]) { 11 | color: currentColor; 12 | } 13 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false, 7 | "targets": { 8 | "esmodules": true 9 | } 10 | } 11 | ], 12 | "@babel/preset-react" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier", "plugin:react/recommended"], 3 | "env": { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "parserOptions": { 8 | "parser": "babel-eslint", 9 | "sourceType": "module", 10 | "ecmaVersion": 2017 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/js/components/Example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Example = ({text, label}) => ( 5 | 8 | ); 9 | 10 | Example.propTypes = { 11 | text: PropTypes.string, 12 | label: PropTypes.string 13 | }; 14 | 15 | export default Example; 16 | -------------------------------------------------------------------------------- /src/js/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Example from './components/Example.js'; 4 | 5 | const App = () => ( 6 |
7 |

Hello, I’m your app, using the minimal React app base.

8 |

9 | Head over to the repo to find out more info. 10 |

11 | 12 |
13 | ); 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Minimal React Base, pal 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /static-assets.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const postcss = require('postcss'); 3 | const atImport = require('postcss-import'); 4 | const cssnano = require('cssnano'); 5 | const isProduction = process.env.NODE_ENV === 'production'; 6 | 7 | const paths = { 8 | css: { 9 | in: './src/css/global.css', 10 | out: './dist/css/global.css' 11 | }, 12 | html: { 13 | in: './src/index.html', 14 | out: './dist/index.html' 15 | } 16 | }; 17 | 18 | const runner = async () => { 19 | const cssSrc = fs.readFileSync(paths.css.in, 'utf8'); 20 | const htmlSrc = fs.readFileSync(paths.html.in, 'utf8'); 21 | 22 | const {css} = await postcss() 23 | .use(atImport()) 24 | .use(cssnano({sourcemap: !isProduction})) 25 | .process(cssSrc, { 26 | from: paths.css.in 27 | }); 28 | 29 | fs.writeFileSync(paths.css.out, css); 30 | fs.writeFileSync(paths.html.out, htmlSrc); 31 | 32 | return true; 33 | }; 34 | 35 | runner(); 36 | module.exports = runner; 37 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const TerserPlugin = require('terser-webpack-plugin'); 2 | 3 | const paths = { 4 | in: `${__dirname}/src/js/main.js`, 5 | out: {path: `${__dirname}/dist/js/`, name: 'main.js'} 6 | }; 7 | 8 | const isProd = process.env.NODE_ENV === 'production'; 9 | 10 | module.exports = { 11 | entry: paths.in, 12 | mode: isProd ? 'production' : 'development', 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.(js)$/, 17 | exclude: /node_modules/, 18 | use: ['babel-loader'] 19 | } 20 | ] 21 | }, 22 | output: { 23 | path: paths.out.path, 24 | filename: paths.out.name 25 | }, 26 | optimization: { 27 | minimizer: [ 28 | new TerserPlugin({ 29 | sourceMap: !isProd 30 | }) 31 | ] 32 | }, 33 | resolve: { 34 | alias: { 35 | 'react': 'preact/compat', 36 | 'react$': 'preact/compat', 37 | 'react-dom': 'preact/compat', 38 | 'react-dom$': 'preact/compat' 39 | } 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | ----------- 3 | 4 | Copyright (c) 2020 Andy Bell (https://hankchizljaw.com) 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # postgres data files 64 | pgdata 65 | 66 | # Vim swap files 67 | .swp 68 | 69 | # Mac slime 70 | .DS_Store 71 | 72 | # Dist 73 | dist -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minimal-react-base", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "prelaunch": "rm -r -f dist && mkdir -p dist/css", 8 | "static": "node static-assets.js", 9 | "start": "npm run prelaunch && concurrently 'webpack --watch' 'watch \"npm run static\" src --ignoreDirectoryPattern /src/js/' 'npx serve ./dist'", 10 | "build": "npm run prelaunch && NODE_ENV=production concurrently 'webpack' 'npm run static'" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/hankchizljaw/minimal-react-base.git" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/hankchizljaw/minimal-react-base/issues" 21 | }, 22 | "homepage": "https://github.com/hankchizljaw/minimal-react-base#readme", 23 | "devDependencies": { 24 | "@babel/core": "^7.7.7", 25 | "@babel/preset-env": "^7.7.7", 26 | "@babel/preset-react": "^7.7.4", 27 | "babel-eslint": "^10.0.3", 28 | "babel-loader": "^8.0.6", 29 | "concurrently": "^5.0.2", 30 | "cssnano": "^4.1.10", 31 | "eslint": "^6.8.0", 32 | "eslint-config-prettier": "^6.9.0", 33 | "eslint-plugin-react": "^7.17.0", 34 | "postcss": "^7.0.26", 35 | "postcss-import": "^12.0.1", 36 | "terser-webpack-plugin": "^2.3.1", 37 | "watch": "^1.0.2", 38 | "webpack": "^4.41.5", 39 | "webpack-cli": "^3.3.10" 40 | }, 41 | "dependencies": { 42 | "preact": "^10.1.1", 43 | "preact-compat": "^3.19.0", 44 | "prop-types": "^15.7.2", 45 | "react": "^16.12.0", 46 | "react-dom": "^16.12.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/css/reset.css: -------------------------------------------------------------------------------- 1 | /* 2 | A modern CSS reset: https://hankchizljaw.com/wrote/a-modern-css-reset/ 3 | */ 4 | 5 | /* Box sizing rules */ 6 | *, 7 | *::before, 8 | *::after { 9 | box-sizing: border-box; 10 | } 11 | 12 | /* Remove default padding */ 13 | ul[class], 14 | ol[class] { 15 | padding: 0; 16 | } 17 | 18 | /* Remove default margin */ 19 | body, 20 | h1, 21 | h2, 22 | h3, 23 | h4, 24 | p, 25 | ul[class], 26 | ol[class], 27 | li, 28 | figure, 29 | figcaption, 30 | blockquote, 31 | dl, 32 | dd { 33 | margin: 0; 34 | } 35 | 36 | /* Set core body defaults */ 37 | body { 38 | min-height: 100vh; 39 | scroll-behavior: smooth; 40 | text-rendering: optimizeSpeed; 41 | line-height: 1.5; 42 | } 43 | 44 | /* Remove list styles on ul, ol elements with a class attribute */ 45 | ul[class], 46 | ol[class] { 47 | list-style: none; 48 | } 49 | 50 | /* A elements that don't have a class get default styles */ 51 | a:not([class]) { 52 | text-decoration-skip-ink: auto; 53 | } 54 | 55 | /* Make images easier to work with */ 56 | img { 57 | max-width: 100%; 58 | display: block; 59 | } 60 | 61 | /* Natural flow and rhythm in articles by default */ 62 | article > * + * { 63 | margin-top: 1em; 64 | } 65 | 66 | /* Inherit fonts for inputs and buttons */ 67 | input, 68 | button, 69 | textarea, 70 | select { 71 | font: inherit; 72 | } 73 | 74 | /* Remove all animations and transitions for people that prefer not to see them */ 75 | @media (prefers-reduced-motion: reduce) { 76 | * { 77 | animation-duration: 0.01ms !important; 78 | animation-iteration-count: 1 !important; 79 | transition-duration: 0.01ms !important; 80 | scroll-behavior: auto !important; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minimal React Base 2 | 3 | This is a little starting point for if you want to sling some React on a HTML page with some CSS. 4 | 5 | The secret though—*[lowers voice]*—is that it actually spits out Preact code instead. Because of that, by default, it’s a ~19kb vs ~131kb (non-gzipped) build. 6 | 7 | [See a live demo](https://minimal-react-base.netlify.com/) 8 | 9 | ## Features 10 | 11 | - Webpack setup for bundling your JavaScript 12 | - Static processor which looks after your HTML and CSS 13 | - PostCSS with `@import` support and a [modern CSS reset](https://github.com/hankchizljaw/modern-css-reset) 14 | 15 | ## Getting started 16 | 17 | 1) First up, clone this repo or grab a zip. 18 | 2) Open up the project and run `npm install` 19 | 3) To run it in dev mode, run `npm start` 20 | 4) To create a production build, run `npm build` 21 | 22 | ## Contributing 23 | 24 | Contribution is more than welcome—especially from those in the community that specialise in React/Preact. 25 | 26 | Please don’t open a PR without first opening an issue to discuss your proposals. Opinionated code changes probably won't get through, but improvements to the project are more than welcome. 27 | 28 | ## Code of Conduct 29 | 30 | ### Our Pledge 31 | 32 | In the interest of fostering an open and welcoming environment, we as 33 | contributors and maintainers pledge to making participation in our project and 34 | our community a harassment-free experience for everyone, regardless of age, body 35 | size, disability, ethnicity, gender identity and expression, level of experience, 36 | nationality, personal appearance, race, religion, or sexual identity and 37 | orientation. 38 | 39 | ### Our Standards 40 | 41 | Examples of behavior that contributes to creating a positive environment 42 | include: 43 | 44 | - Using welcoming and inclusive language 45 | - Being respectful of differing viewpoints and experiences 46 | - Gracefully accepting constructive criticism 47 | - Focusing on what is best for the community 48 | - Showing empathy towards other community members 49 | 50 | Examples of unacceptable behavior by participants include: 51 | 52 | - The use of sexualized language or imagery and unwelcome sexual attention or 53 | advances 54 | - Trolling, insulting/derogatory comments, and personal or political attacks 55 | - Public or private harassment 56 | - Publishing others' private information, such as a physical or electronic 57 | address, without explicit permission 58 | - Other conduct which could reasonably be considered inappropriate in a 59 | professional setting 60 | 61 | ### Our Responsibilities 62 | 63 | Project maintainers are responsible for clarifying the standards of acceptable 64 | behavior and are expected to take appropriate and fair corrective action in 65 | response to any instances of unacceptable behavior. 66 | 67 | Project maintainers have the right and responsibility to remove, edit, or 68 | reject comments, commits, code, wiki edits, issues, and other contributions 69 | that are not aligned to this Code of Conduct, or to ban temporarily or 70 | permanently any contributor for other behaviors that they deem inappropriate, 71 | threatening, offensive, or harmful. 72 | 73 | ### Scope 74 | 75 | This Code of Conduct applies both within project spaces and in public spaces 76 | when an individual is representing the project or its community. Examples of 77 | representing a project or community include using an official project e-mail 78 | address, posting via an official social media account, or acting as an appointed 79 | representative at an online or offline event. Representation of a project may be 80 | further defined and clarified by project maintainers. 81 | 82 | ### Enforcement 83 | 84 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 85 | reported by contacting the project team at me@andy-bell.design. All 86 | complaints will be reviewed and investigated and will result in a response that 87 | is deemed necessary and appropriate to the circumstances. The project team is 88 | obligated to maintain confidentiality with regard to the reporter of an incident. 89 | Further details of specific enforcement policies may be posted separately. 90 | 91 | Project maintainers who do not follow or enforce the Code of Conduct in good 92 | faith may face temporary or permanent repercussions as determined by other 93 | members of the project's leadership. 94 | 95 | ### Attribution 96 | 97 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 98 | available at [http://contributor-covenant.org/version/1/4][version] 99 | 100 | [homepage]: http://contributor-covenant.org 101 | [version]: http://contributor-covenant.org/version/1/4/ 102 | 103 | --------------------------------------------------------------------------------