├── .env.sample
├── .eslintignore
├── .gitignore
├── src
├── stylesheets
│ ├── top-wrappers.css
│ ├── themes
│ │ ├── green.scss
│ │ └── default.scss
│ ├── color.scss
│ └── reset.css
├── components
│ ├── app
│ │ ├── index.scss
│ │ ├── test
│ │ │ └── app.test.js
│ │ └── index.js
│ ├── font-loader
│ │ └── index.js
│ ├── home-screen
│ │ ├── test
│ │ │ └── home-screen.test.js
│ │ ├── index.js
│ │ └── index.scss
│ └── counter
│ │ ├── index.scss
│ │ ├── test
│ │ └── counter.test.js
│ │ └── index.js
├── routes.js
├── index.tpl.html
└── boot.js
├── .editorconfig
├── test
├── configuration.js
└── utils.js
├── lib
└── read-theme.js
├── bin
├── publish-gh-page
└── server.js
├── package.json
├── webpack.development.babel.js
├── webpack.production.babel.js
├── README.md
└── .eslintrc.json
/.env.sample:
--------------------------------------------------------------------------------
1 | FOO=bar
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | .DS_Store
4 | *.log
5 | .sass-cache
6 | *.map
7 | .env
8 | .eslintcache
9 |
--------------------------------------------------------------------------------
/src/stylesheets/top-wrappers.css:
--------------------------------------------------------------------------------
1 | /* Top wrapper styles */
2 | /* ================== */
3 |
4 | html,
5 | body,
6 | .main-wrapper {
7 | height: 100%;
8 | }
9 |
--------------------------------------------------------------------------------
/src/stylesheets/themes/green.scss:
--------------------------------------------------------------------------------
1 | // Green theme
2 | // ===========
3 |
4 | $color-palette: (
5 | 'first': #008542,
6 | 'second': #ffffff
7 | );
8 |
9 | $step-size: 10% !default;
10 |
--------------------------------------------------------------------------------
/src/stylesheets/themes/default.scss:
--------------------------------------------------------------------------------
1 | // Default theme
2 | // =============
3 |
4 | $color-palette: (
5 | 'first': #d92500,
6 | 'second': #ffffff
7 | );
8 |
9 | $step-size: 10% !default;
10 |
--------------------------------------------------------------------------------
/src/components/app/index.scss:
--------------------------------------------------------------------------------
1 | // Root app wrapper styles
2 | // =======================
3 |
4 | .App {
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | height: 100%;
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/font-loader/index.js:
--------------------------------------------------------------------------------
1 | // Font loader
2 | // ===========
3 |
4 | import WebFont from 'webfontloader'
5 |
6 | export default function loadFonts() {
7 | WebFont.load({
8 | google: { families: ['Fira Mono'] }
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/test/configuration.js:
--------------------------------------------------------------------------------
1 | // Test suite configuration
2 | // ========================
3 |
4 | import hook from 'css-modules-require-hook'
5 |
6 | hook({
7 | extensions: ['.scss', '.css'],
8 | preprocessCss: function() {
9 | return ''
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/lib/read-theme.js:
--------------------------------------------------------------------------------
1 | // Read theme
2 | // ==========
3 | //
4 | // Reads a styleguide theme file and returns it.
5 |
6 | import fs from 'fs'
7 |
8 | export default function readTheme(name) {
9 | const path = `${__dirname}/../src/stylesheets/themes/${name}.scss`
10 | return fs.readFileSync(path).toString()
11 | }
12 |
--------------------------------------------------------------------------------
/test/utils.js:
--------------------------------------------------------------------------------
1 | // Test utilities
2 | // ==============
3 |
4 | import TestUtils from 'react-addons-test-utils'
5 |
6 | export function shallowlyRenderedOutput(Component) {
7 | const shallowRenderer = TestUtils.createRenderer()
8 | shallowRenderer.render(Component)
9 |
10 | return shallowRenderer.getRenderOutput()
11 | }
12 |
--------------------------------------------------------------------------------
/src/routes.js:
--------------------------------------------------------------------------------
1 | // Client routes
2 | // =============
3 |
4 | import React from 'react'
5 | import { Route } from 'react-router'
6 | import App from './components/app'
7 | import HomeScreen from './components/home-screen'
8 |
9 | export default
10 |
11 |
12 |
--------------------------------------------------------------------------------
/bin/publish-gh-page:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | npm run build
4 | rm -rf gh-pages
5 | mkdir gh-pages
6 | cd gh-pages
7 | cp -r ../build/* .
8 | git init .
9 | git remote add origin git@github.com:juliocesar/neob.git
10 | git checkout -b gh-pages
11 | git add .
12 | git commit -m 'Publishing...'
13 | git push origin gh-pages -f
14 | cd ..
15 | rm -rf gh-pages
16 |
--------------------------------------------------------------------------------
/src/components/app/test/app.test.js:
--------------------------------------------------------------------------------
1 | // App component test
2 | // ==================
3 |
4 | import React from 'react'
5 | import App from '../'
6 | import test from 'ava'
7 | import { shallow } from 'enzyme'
8 |
9 | test('App should be a div because this is a dumb test', t => {
10 | const component = shallow()
11 | t.is(component.node.type, 'div')
12 | })
13 |
--------------------------------------------------------------------------------
/src/components/app/index.js:
--------------------------------------------------------------------------------
1 | // App container
2 | // =============
3 |
4 | import React from 'react'
5 | import style from './index.scss'
6 |
7 | export default class App extends React.Component {
8 | render() {
9 | return
10 | {this.props.children}
11 |
12 | }
13 | }
14 |
15 | App.propTypes = {
16 | children: React.PropTypes.node
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/home-screen/test/home-screen.test.js:
--------------------------------------------------------------------------------
1 | // HomeScreen component test
2 | // =========================
3 |
4 | import React from 'react'
5 | import HomeScreen from '../'
6 | import test from 'ava'
7 | import { shallow } from 'enzyme'
8 |
9 | test('HomeScreen example test. Do not do this, for real', t => {
10 | const component = shallow()
11 | t.is(component.find('h1').length, 1)
12 | })
13 |
--------------------------------------------------------------------------------
/src/index.tpl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | neob ― yet another Webpack boilerplate
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/boot.js:
--------------------------------------------------------------------------------
1 | // Client boot file
2 | // ================
3 |
4 | import './stylesheets/reset.css'
5 | import './stylesheets/top-wrappers.css'
6 | import 'babel-polyfill'
7 | import React from 'react'
8 | import { render } from 'react-dom'
9 | import { Router, hashHistory as history } from 'react-router'
10 | import routes from './routes'
11 | import loadFonts from './components/font-loader'
12 |
13 | loadFonts()
14 |
15 | render(
16 | ,
17 | document.querySelector('.main-wrapper')
18 | )
19 |
--------------------------------------------------------------------------------
/src/stylesheets/color.scss:
--------------------------------------------------------------------------------
1 | // Colour scale function
2 | // =====================
3 |
4 | @function color($name, $scale: 0) {
5 | @if ($scale > 5 or $scale < -5) {
6 | @error "Scale argument cannot be #{$scale}. Passed with #{$name}";
7 | }
8 |
9 | $val: map-get($color-palette, $name);
10 |
11 | @if $val {
12 | @if $scale < 0 {
13 | @return mix(white, $val, abs($scale) * $step-size);
14 | } @else {
15 | @return mix(black, $val, $scale * $step-size);
16 | }
17 | } @else {
18 | @error "Couldn't find color with name #{$name}";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/home-screen/index.js:
--------------------------------------------------------------------------------
1 | // Home screen
2 | // ===========
3 |
4 | import React from 'react'
5 | import Counter from '../counter'
6 | import style from './index.scss'
7 |
8 | export default class HomeScreen extends React.Component {
9 | render() {
10 | return
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/counter/index.scss:
--------------------------------------------------------------------------------
1 | // Counter styles
2 | // ==============
3 |
4 | @import 'dirg';
5 | @import 'color';
6 |
7 | .Counter {
8 | display: flex;
9 | flex-flow: column;
10 | align-items: center;
11 | cursor: pointer;
12 | color: color('first');
13 |
14 | >* + * {
15 | margin-top: units(1);
16 | }
17 | }
18 |
19 | .label {
20 | @include font-size(0);
21 | }
22 |
23 | .countLabel {
24 | @include font-size(4);
25 | font-weight: bold;
26 | width: units(4);
27 | height: units(4);
28 | line-height: units(4);
29 | border-radius: 100%;
30 | background: color('first', -5);
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/counter/test/counter.test.js:
--------------------------------------------------------------------------------
1 | // Counter component test
2 | // ======================
3 |
4 | import React from 'react'
5 | import Counter from '../'
6 | import test from 'ava'
7 | import { shallow } from 'enzyme'
8 |
9 | test('Counter defaults to 0 counts', t => {
10 | const component = shallow()
11 | t.is(component.state('count'), 0)
12 | })
13 |
14 | test('Counter increases count when clicked', t => {
15 | const component = shallow()
16 | const currentCount = component.state('count')
17 | component.simulate('click')
18 | t.is(component.state('count'), currentCount + 1)
19 | })
20 |
--------------------------------------------------------------------------------
/src/components/home-screen/index.scss:
--------------------------------------------------------------------------------
1 | // Home screen styles
2 | // ==================
3 |
4 | @import 'dirg';
5 | @import 'color';
6 |
7 | .HomeScreen {
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | flex-direction: column;
12 | width: 100%;
13 | height: 100%;
14 | text-align: center;
15 | font-family: 'Fira Mono';
16 | }
17 |
18 | .heading {
19 | @include font-size(4);
20 | color: color('first');
21 | }
22 |
23 | .heading + * {
24 | margin-top: units(1);
25 | }
26 |
27 | .sourceLink {
28 | @include font-size(0);
29 | color: color('first');
30 | }
31 |
32 | * + .sourceLink {
33 | margin-top: units(2);
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/counter/index.js:
--------------------------------------------------------------------------------
1 | // Counter component
2 | // =================
3 |
4 | import React from 'react'
5 | import style from './index.scss'
6 |
7 | export default class Counter extends React.Component {
8 | constructor(options) {
9 | super(options)
10 | this.state = { count: this.props.count }
11 | this.onClick = this.onClick.bind(this)
12 | }
13 |
14 | onClick() {
15 | this.setState({ count: this.state.count + 1 })
16 | }
17 |
18 | render() {
19 | return
20 | {this.state.count}
21 |
22 | Click the number to increase the count
23 |
24 |
25 | }
26 | }
27 |
28 | Counter.propTypes = {
29 | count: React.PropTypes.number.isRequired
30 | }
31 |
32 | Counter.defaultProps = {
33 | count: 0
34 | }
35 |
--------------------------------------------------------------------------------
/bin/server.js:
--------------------------------------------------------------------------------
1 | // Development server
2 | // ==================
3 |
4 | import path from 'path'
5 | import express from 'express'
6 | import webpack from 'webpack'
7 | import webpackDevMiddleware from 'webpack-dev-middleware'
8 | import webpackHotMiddleWare from 'webpack-hot-middleware'
9 | import config from '../webpack.development.babel'
10 |
11 | const env = process.env.NODE_ENV || 'development'
12 | const app = express()
13 | const compiler = webpack(config)
14 |
15 | if (env === 'development') {
16 | app.use(webpackDevMiddleware(compiler, {
17 | publicPath: config.output.publicPath,
18 | contentBase: 'src',
19 | stats: {
20 | colors: true,
21 | hash: false,
22 | timings: true,
23 | chunks: false,
24 | chunkModules: false,
25 | modules: false
26 | }
27 | }))
28 | app.use(webpackHotMiddleWare(compiler))
29 | } else {
30 | app.get('*', function(req, res) {
31 | res.sendFile(path.join(__dirname, 'build/index.html'))
32 | })
33 | }
34 |
35 | app.use(express.static(path.join(__dirname, '/build')))
36 |
37 | app.listen(config._hotPort, 'localhost', (err) => {
38 | if (err) console.log(err)
39 | console.info(` ==> Listening on port ${config._hotPort}`)
40 | })
41 |
--------------------------------------------------------------------------------
/src/stylesheets/reset.css:
--------------------------------------------------------------------------------
1 | /* Reset extracted from Compass reset */
2 |
3 | html, body, div, span, applet, object, iframe,
4 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
5 | a, abbr, acronym, address, big, cite, code,
6 | del, dfn, em, img, ins, kbd, q, s, samp,
7 | small, strike, strong, sub, sup, tt, var,
8 | b, u, i, center,
9 | dl, dt, dd, ol, ul, li,
10 | fieldset, form, label, legend,
11 | table, caption, tbody, tfoot, thead, tr, th, td,
12 | article, aside, canvas, details, embed,
13 | figure, figcaption, footer, header, hgroup,
14 | menu, nav, output, ruby, section, summary,
15 | time, mark, audio, video {
16 | margin: 0;
17 | padding: 0;
18 | border: 0;
19 | font: inherit;
20 | font-size: 100%;
21 | vertical-align: baseline; }
22 |
23 | html {
24 | line-height: 1; }
25 |
26 | ol, ul {
27 | list-style: none; }
28 |
29 | table {
30 | border-collapse: collapse;
31 | border-spacing: 0; }
32 |
33 | caption, th, td {
34 | text-align: left;
35 | font-weight: normal;
36 | vertical-align: middle; }
37 |
38 | q, blockquote {
39 | quotes: none; }
40 | q:before, q:after, blockquote:before, blockquote:after {
41 | content: "";
42 | content: none; }
43 |
44 | a img {
45 | border: none; }
46 |
47 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary {
48 | display: block; }
49 |
50 |
51 | /* Further resets custom added */
52 | input,
53 | select,
54 | textarea,
55 | button {
56 | -webkit-appearance: none;
57 | -moz-appearance: none;
58 | font-family: inherit;
59 | padding: 0;
60 | margin: 0;
61 | border-radius: 0;
62 | border: 0;
63 | background: none;
64 | outline: none;
65 | }
66 |
67 | a,
68 | button {
69 | cursor: pointer;
70 | }
71 |
72 | *,
73 | *:before,
74 | *:after {
75 | box-sizing: border-box;
76 | }
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "test": "ava",
4 | "start": "babel-node ./bin/server",
5 | "build": "rimraf build && NODE_ENV=production webpack --config ./webpack.production.babel.js --progress --profile --colors",
6 | "gh-page": "./bin/publish-gh-page",
7 | "eslint": "eslint . --ext .js --cache"
8 | },
9 | "ava": {
10 | "files": [
11 | "src/components/**/*.test.js"
12 | ],
13 | "failFast": true,
14 | "verbose": true,
15 | "require": [
16 | "babel-core/register",
17 | "./test/configuration.js"
18 | ],
19 | "babel": "inherit"
20 | },
21 | "dependencies": {
22 | "react": "^15.1.0",
23 | "react-dom": "^15.1.0",
24 | "react-router": "^2.4.1",
25 | "webfontloader": "^1.6.24"
26 | },
27 | "devDependencies": {
28 | "autoprefixer": "^6.3.6",
29 | "ava": "^0.15.2",
30 | "babel-cli": "^6.10.1",
31 | "babel-core": "^6.9.1",
32 | "babel-loader": "^6.2.4",
33 | "babel-plugin-react-transform": "^2.0.2",
34 | "babel-polyfill": "^6.9.1",
35 | "babel-preset-es2015": "^6.9.0",
36 | "babel-preset-react": "^6.5.0",
37 | "babel-preset-stage-2": "^6.5.0",
38 | "css-loader": "^0.23.1",
39 | "css-modules-require-hook": "^4.0.1",
40 | "dirg": "git+https://github.com/juliocesar/dirg.git",
41 | "dotenv": "^2.0.0",
42 | "enzyme": "^2.3.0",
43 | "eslint": "^2.12.0",
44 | "eslint-config-defaults": "^9.0.0",
45 | "eslint-loader": "^1.3.0",
46 | "eslint-plugin-ava": "^2.5.0",
47 | "eslint-plugin-promise": "^1.3.2",
48 | "eslint-plugin-react": "^5.2.1",
49 | "express": "^4.14.0",
50 | "extract-text-webpack-plugin": "^1.0.1",
51 | "file-loader": "^0.8.5",
52 | "html-webpack-plugin": "^2.21.0",
53 | "json-loader": "^0.5.4",
54 | "node-sass": "^3.7.0",
55 | "postcss-loader": "^0.9.1",
56 | "postcss-modules-values": "^1.1.3",
57 | "react-addons-test-utils": "^15.1.0",
58 | "react-transform-catch-errors": "^1.0.2",
59 | "react-transform-hmr": "^1.0.4",
60 | "redbox-react": "^1.2.6",
61 | "rimraf": "^2.5.2",
62 | "sass-loader": "^3.2.0",
63 | "sinon": "^1.17.4",
64 | "stats-webpack-plugin": "^0.3.1",
65 | "style-loader": "^0.13.1",
66 | "webpack": "^1.13.1",
67 | "webpack-dev-middleware": "^1.6.1",
68 | "webpack-hot-middleware": "^2.10.0"
69 | },
70 | "babel": {
71 | "presets": [
72 | "es2015",
73 | "react",
74 | "stage-2"
75 | ]
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/webpack.development.babel.js:
--------------------------------------------------------------------------------
1 | // Webpack development config file
2 | // ===============================
3 |
4 | import path from 'path'
5 | import webpack from 'webpack'
6 | import HtmlWebpackPlugin from 'html-webpack-plugin'
7 | import dotenv from 'dotenv'
8 | import readTheme from './lib/read-theme'
9 | import { includePaths } from 'dirg'
10 |
11 | dotenv.load()
12 |
13 | module.exports = {
14 | devtool: 'eval',
15 | entry: [
16 | 'webpack-hot-middleware/client',
17 | path.join(__dirname, 'src/boot.js')
18 | ],
19 | output: {
20 | path: path.join(__dirname, '/build/'),
21 | filename: '[name].js',
22 | publicPath: '/'
23 | },
24 | plugins: [
25 | new HtmlWebpackPlugin({
26 | template: 'src/index.tpl.html',
27 | inject: 'body',
28 | filename: 'index.html'
29 | }),
30 | new webpack.optimize.OccurenceOrderPlugin(),
31 | new webpack.HotModuleReplacementPlugin(),
32 | new webpack.NoErrorsPlugin(),
33 | new webpack.DefinePlugin({
34 | 'process.env.NODE_ENV': JSON.stringify('development'),
35 | '__DEV__': JSON.stringify(process.env.NODE_ENV)
36 | })
37 | ],
38 | module: {
39 | preLoaders: [
40 | {
41 | test: /(\.jsx|\.js)$/,
42 | loader: 'eslint'
43 | }
44 | ],
45 | loaders: [
46 | {
47 | test: /(\.jsx|\.js)$/,
48 | exclude: /node_modules/,
49 | loader: 'babel'
50 | },
51 | {
52 | test: /\.json?$/,
53 | exclude: /node_modules/,
54 | loader: 'json'
55 | },
56 | {
57 | test: /\.css$/,
58 | exclude: /node_modules/,
59 | loader: 'style!css'
60 | },
61 | {
62 | test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
63 | exclude: /node_modules/,
64 | loader: 'file-loader'
65 | },
66 | {
67 | test: /\.scss$/,
68 | exclude: /node_modules/,
69 | loader: [
70 | 'style',
71 | [
72 | 'css?importLoaders=1',
73 | 'modules',
74 | 'localIdentName=[name]---[local]---[hash:base64:5]'
75 | ].join('&'),
76 | 'sass'
77 | ].join('!')
78 | }
79 | ]
80 | },
81 | postcss: [
82 | require('postcss-modules-values')
83 | ],
84 | sassLoader: {
85 | includePaths: [
86 | path.resolve(__dirname, 'src/stylesheets'),
87 | ...includePaths
88 | ],
89 | data: [readTheme(process.env.THEME || 'default')]
90 | },
91 | _hotPort: 4567
92 | }
93 |
--------------------------------------------------------------------------------
/webpack.production.babel.js:
--------------------------------------------------------------------------------
1 | // Webpack development config file
2 | // ===============================
3 |
4 | import path from 'path'
5 | import webpack from 'webpack'
6 | import HtmlWebpackPlugin from 'html-webpack-plugin'
7 | import ExtractTextPlugin from 'extract-text-webpack-plugin'
8 | import StatsPlugin from 'stats-webpack-plugin'
9 | import dotenv from 'dotenv'
10 | import readTheme from './lib/read-theme'
11 | import { includePaths } from 'dirg'
12 |
13 | dotenv.load()
14 |
15 | const CSSLoaders = [
16 | 'css?importLoaders=1',
17 | 'modules&localIdentName=[name]---[local]---[hash:base64:5]!sass'
18 | ].join('&')
19 |
20 | module.exports = {
21 | devtool: 'cheap-source-map',
22 | entry: [
23 | path.join(__dirname, 'src/boot.js')
24 | ],
25 | output: {
26 | path: path.join(__dirname, '/build/'),
27 | filename: '[name]-[hash].min.js'
28 | },
29 | plugins: [
30 | new webpack.optimize.OccurenceOrderPlugin(),
31 | new HtmlWebpackPlugin({
32 | template: 'src/index.tpl.html',
33 | inject: 'body',
34 | filename: 'index.html'
35 | }),
36 | new ExtractTextPlugin('[name]-[hash].min.css'),
37 | new webpack.optimize.UglifyJsPlugin({
38 | compressor: {
39 | warnings: false,
40 | screw_ie8: true
41 | }
42 | }),
43 | new webpack.optimize.DedupePlugin(),
44 | new StatsPlugin('webpack.stats.json', { source: false, modules: false }),
45 | new webpack.DefinePlugin({
46 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
47 | '__DEV__': JSON.stringify(process.env.NODE_ENV)
48 | })
49 | ],
50 | module: {
51 | loaders: [
52 | {
53 | test: /\.js?$/,
54 | exclude: /node_modules/,
55 | loader: 'babel'
56 | },
57 | {
58 | test: /\.json?$/,
59 | loader: 'json'
60 | },
61 | {
62 | test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
63 | loader: 'file-loader'
64 | },
65 | {
66 | test: /\.css$/,
67 | loader: ExtractTextPlugin.extract('style', 'css')
68 | },
69 | {
70 | test: /\.scss$/,
71 | loader: ExtractTextPlugin.extract('style', CSSLoaders)
72 | }
73 | ]
74 | },
75 | sassLoader: {
76 | includePaths: [
77 | path.resolve(__dirname, 'src/stylesheets'),
78 | ...includePaths
79 | ],
80 | data: [readTheme(process.env.THEME || 'default')]
81 | },
82 | postcss: [
83 | require('autoprefixer'),
84 | require('postcss-modules-values')
85 | ]
86 | }
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Neob
2 |
3 | A React/Webpack/(S)CSS Modules/testing applications boilerplate.
4 |
5 | ## Features
6 |
7 | * Webpack + hot-module-replacement ([babel-plugin-react-transform](https://github.com/gaearon/babel-plugin-react-transform) + [react-transform-hmr](https://github.com/gaearon/react-transform-hmr)).
8 | * Production optimisations settings/plugins for Webpack.
9 | * Modular component organisation. Component folders in `src/components` carry code, styles and tests.
10 | * [css-modules](https://github.com/css-modules/css-modules/) + [SCSS](https://github.com/jtangelder/sass-loader) + [PostCSS](https://github.com/postcss/postcss).
11 | * **Themes** using colour scales. Single mixin for consolidating colour sourcing.
12 | * **Sizing scales** (vertical rhythm and other goodies) done using [dirg](https://github.com/juliocesar/dirg).
13 | * Basic routing setup with [react-router](https://github.com/reactjs/react-router).
14 | * Tests using [Ava](https://github.com/avajs/ava/) and [Enzyme](http://airbnb.io/enzyme/).
15 | * ESLint, with a slightly modified version of the [Feross Standard](https://github.com/feross/standard) as default, extended to include test files linting. Drop your own `.eslintrc.json` file on top of it if you don’t like it.
16 | * **No Gulp or Grunt**. Automation is done by commands in `package.json`. More complex stuff done by scripts in `bin`.
17 |
18 | ## Usage
19 |
20 | * Start the dev server:
21 |
22 | ```
23 | $ npm start
24 | ```
25 |
26 | * Build to static files in `build`:
27 |
28 | ```
29 | $ npm run build
30 | ```
31 |
32 | * Compile static application and deploy it to GitHub pages (update `bin/publish-gh-page` with your repository’s actual address):
33 |
34 | ```
35 | $ ./bin/publish-gh-page
36 | ```
37 |
38 | ## Themes
39 |
40 | Themes are SCSS hashes kept in `src/stylesheets/themes`. The [color mixin](https://github.com/juliocesar/neob/blob/master/src/stylesheets/color.scss) then sources them by key, and allows for 5 tint steps towards darker or lighter, allowing controlled variations for each entry.
41 |
42 | By default, the build will look for a `default.scss` file. Alternative files can be loaded by passing an environment variable `THEME`:
43 |
44 | ```
45 | $ THEME=green npm start
46 | ```
47 |
48 | Or build to static files using a specific theme:
49 |
50 | ```
51 | $ THEME=green npm run build
52 | ```
53 | ## Grid
54 |
55 | Refer to [dirg](https://github.com/juliocesar/dirg)’s documentation, or [the longer post](https://medium.com/@julio_ody/sizing-supra-summa-3701cd075244#.dhlhjf6vy) on it.
56 |
57 | You can override the default scale by supplying one in a separate file. Create (for example), a file `src/stylesheets/dirg-scale.scss` with:
58 |
59 | ```
60 | $dirg-scales: (
61 | default: (
62 | font-size: 16px,
63 | unit: 21px,
64 | factor: 1.35
65 | )
66 | );
67 |
68 | ```
69 |
70 | Then source it right before dirg:
71 |
72 | ```
73 | @import 'dirg-scale';
74 | @import 'dirg';
75 | …
76 | ```
77 |
78 | # Credits (y u no fork)
79 |
80 | neob is based off of [react-kickstart](https://github.com/vesparny/react-kickstart). I wasn’t sure initially how much it’d look like it, so I went with a copy first. It has now diverged sufficiently anyway to become more than just a build boilerplate, but introduce a few helpers for building applications.
81 |
82 | # License
83 |
84 | MIT.
85 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "ecmaVersion": 6,
4 | "ecmaFeatures": {
5 | "experimentalObjectRestSpread": true,
6 | "jsx": true
7 | },
8 | "sourceType": "module"
9 | },
10 |
11 | "env": {
12 | "es6": true,
13 | "node": true
14 | },
15 |
16 | "plugins": [
17 | "promise",
18 | "react",
19 | "ava"
20 | ],
21 |
22 | "globals": {
23 | "document": false,
24 | "navigator": false,
25 | "window": false
26 | },
27 |
28 | "rules": {
29 | "accessor-pairs": 2,
30 | "array-bracket-spacing": [2, "never"],
31 | "arrow-spacing": [2, { "before": true, "after": true }],
32 | "block-spacing": [2, "always"],
33 | "brace-style": [2, "1tbs", { "allowSingleLine": true }],
34 | "camelcase": [2, { "properties": "never" }],
35 | "comma-dangle": [2, "never"],
36 | "comma-spacing": [2, { "before": false, "after": true }],
37 | "comma-style": [2, "last"],
38 | "computed-property-spacing": [2, "never"],
39 | "constructor-super": 2,
40 | "curly": [2, "multi-line"],
41 | "dot-location": [2, "property"],
42 | "eol-last": 2,
43 | "eqeqeq": [2, "allow-null"],
44 | "generator-star-spacing": [2, { "before": true, "after": true }],
45 | "handle-callback-err": [2, "^(err|error)$" ],
46 | "indent": [2, 2, { "SwitchCase": 1 }],
47 | "jsx-quotes": [2, "prefer-double"],
48 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
49 | "keyword-spacing": [2, { "before": true, "after": true }],
50 | "max-len": [2, 80],
51 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }],
52 | "new-parens": 2,
53 | "no-array-constructor": 2,
54 | "no-caller": 2,
55 | "no-class-assign": 2,
56 | "no-cond-assign": 2,
57 | "no-const-assign": 2,
58 | "no-control-regex": 2,
59 | "no-debugger": 2,
60 | "no-delete-var": 2,
61 | "no-dupe-args": 2,
62 | "no-dupe-class-members": 2,
63 | "no-dupe-keys": 2,
64 | "no-duplicate-case": 2,
65 | "no-duplicate-imports": 2,
66 | "no-empty-character-class": 2,
67 | "no-empty-pattern": 2,
68 | "no-eval": 2,
69 | "no-ex-assign": 2,
70 | "no-extend-native": 2,
71 | "no-extra-bind": 2,
72 | "no-extra-boolean-cast": 2,
73 | "no-extra-parens": [2, "functions"],
74 | "no-fallthrough": 2,
75 | "no-floating-decimal": 2,
76 | "no-func-assign": 2,
77 | "no-implied-eval": 2,
78 | "no-inner-declarations": [2, "functions"],
79 | "no-invalid-regexp": 2,
80 | "no-irregular-whitespace": 2,
81 | "no-iterator": 2,
82 | "no-label-var": 2,
83 | "no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
84 | "no-lone-blocks": 2,
85 | "no-mixed-spaces-and-tabs": 2,
86 | "no-multi-spaces": 2,
87 | "no-multi-str": 2,
88 | "no-multiple-empty-lines": [2, { "max": 1 }],
89 | "no-native-reassign": 2,
90 | "no-negated-in-lhs": 2,
91 | "no-new": 2,
92 | "no-new-func": 2,
93 | "no-new-object": 2,
94 | "no-new-require": 2,
95 | "no-new-symbol": 2,
96 | "no-new-wrappers": 2,
97 | "no-obj-calls": 2,
98 | "no-octal": 2,
99 | "no-octal-escape": 2,
100 | "no-path-concat": 2,
101 | "no-proto": 2,
102 | "no-redeclare": 2,
103 | "no-regex-spaces": 2,
104 | "no-return-assign": [2, "except-parens"],
105 | "no-self-assign": 2,
106 | "no-self-compare": 2,
107 | "no-sequences": 2,
108 | "no-shadow-restricted-names": 2,
109 | "no-spaced-func": 2,
110 | "no-sparse-arrays": 2,
111 | "no-this-before-super": 2,
112 | "no-throw-literal": 2,
113 | "no-trailing-spaces": 2,
114 | "no-undef": 2,
115 | "no-undef-init": 2,
116 | "no-unexpected-multiline": 2,
117 | "no-unmodified-loop-condition": 2,
118 | "no-unneeded-ternary": [2, { "defaultAssignment": false }],
119 | "no-unreachable": 2,
120 | "no-unsafe-finally": 2,
121 | "no-unused-vars": [2, { "vars": "all", "args": "none" }],
122 | "no-useless-call": 2,
123 | "no-useless-computed-key": 2,
124 | "no-useless-constructor": 2,
125 | "no-useless-escape": 2,
126 | "no-whitespace-before-property": 2,
127 | "no-with": 2,
128 | "object-curly-spacing": [2, "always"],
129 | "one-var": [2, { "initialized": "never" }],
130 | "operator-linebreak": [2, "after"],
131 | "padded-blocks": [2, "never"],
132 | "quotes": [2, "single", "avoid-escape"],
133 | "semi": [2, "never"],
134 | "semi-spacing": [2, { "before": false, "after": true }],
135 | "space-before-blocks": [2, "always"],
136 | "space-in-parens": [2, "never"],
137 | "space-infix-ops": 2,
138 | "space-unary-ops": [2, { "words": true, "nonwords": false }],
139 | "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
140 | "template-curly-spacing": [2, "never"],
141 | "use-isnan": 2,
142 | "valid-typeof": 2,
143 | "wrap-iife": [2, "any"],
144 | "yield-star-spacing": [2, "both"],
145 | "yoda": [2, "never"],
146 |
147 | "react/jsx-no-undef": 1,
148 | "react/jsx-uses-react": 1,
149 | "react/jsx-uses-vars": 1,
150 | "react/jsx-no-bind": 2,
151 | "react/no-did-update-set-state": 2,
152 | "react/no-unknown-property": 2,
153 | "react/prop-types": 2,
154 |
155 | "promise/param-names": 2,
156 |
157 | "ava/assertion-arguments": "error",
158 | "ava/assertion-message": ["off", "always"],
159 | "ava/max-asserts": ["error", 2],
160 | "ava/no-cb-test": "error",
161 | "ava/no-identical-title": "error",
162 | "ava/no-ignored-test-files": "error",
163 | "ava/no-invalid-end": "error",
164 | "ava/no-only-test": "error",
165 | "ava/no-skip-assert": "error",
166 | "ava/no-skip-test": "error",
167 | "ava/no-statement-after-end": "error",
168 | "ava/no-todo-test": "warn",
169 | "ava/no-unknown-modifiers": "error",
170 | "ava/prefer-power-assert": "off",
171 | "ava/test-ended": "error",
172 | "ava/test-title": ["error", "if-multiple"],
173 | "ava/use-t-well": "error",
174 | "ava/use-t": "error",
175 | "ava/use-test": "error",
176 | "ava/use-true-false": "error"
177 | }
178 | }
179 |
--------------------------------------------------------------------------------