diff --git a/.editorconfig b/.editorconfig
index 3b18034..013c8c5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,5 +9,5 @@ insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
-[*.css]
+[*.css,*.scss]
indent_size = 4
diff --git a/.stylelintrc b/.stylelintrc
index 45e9838..38d2566 100644
--- a/.stylelintrc
+++ b/.stylelintrc
@@ -1,6 +1,7 @@
{
"extends": [
"stylelint-config-standard",
+ "stylelint-config-css-modules",
"./node_modules/prettier-stylelint/config.js"
],
"rules": {
diff --git a/package.json b/package.json
index 6b94604..8bed593 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"scripts": {
"test": "eslint ./src/**/*.{js,jsx} && stylelint ./src/**/*.css",
"start": "webpack-dev-server --hot --host 0.0.0.0 --config=./webpack.dev.config.js",
- "build": "webpack --mode production --config webpack.common.config.js",
+ "build": "cross-env NODE_ENV=production webpack --config webpack.common.config.js",
"package": "electron-builder build -mwl && open ./dist",
"dist": "rm -rf {dist,build}/ && npm run build && npm run package",
"release": "release-it --non-interactive"
@@ -38,6 +38,7 @@
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.6",
"copy-webpack-plugin": "^5.0.3",
+ "cross-env": "^5.2.0",
"css-loader": "^2.1.1",
"electron": "^5.0.2",
"electron-builder": "^20.43.0",
@@ -48,14 +49,18 @@
"eslint-plugin-react": "^7.13.0",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
+ "mini-css-extract-plugin": "^0.7.0",
+ "node-sass": "^4.12.0",
"prettier": "^1.17.1",
"prettier-stylelint": "^0.4.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-pose": "^4.0.8",
"release-it": "^12.2.2",
+ "sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"stylelint": "^10.0.1",
+ "stylelint-config-css-modules": "^1.4.0",
"stylelint-config-standard": "^18.3.0",
"webpack": "^4.33.0",
"webpack-cli": "^3.3.2",
diff --git a/src/renderer/components/Balance.jsx b/src/renderer/components/Balance.jsx
index 12019c9..ee0feac 100644
--- a/src/renderer/components/Balance.jsx
+++ b/src/renderer/components/Balance.jsx
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import posed, { PoseGroup } from 'react-pose'
import { AppContext } from '../store/createContext'
import { cryptoFormatter } from '../../utils'
-import './Balance.css'
+import styles from './Balance.module.scss'
import { fadeIn } from './Animations'
const Animation = posed.h1(fadeIn)
@@ -21,7 +21,7 @@ export default class Balance extends PureComponent {
return (
-
+
{cryptoFormatter(balance[currency], currency)}
diff --git a/src/renderer/components/Balance.css b/src/renderer/components/Balance.module.scss
similarity index 84%
rename from src/renderer/components/Balance.css
rename to src/renderer/components/Balance.module.scss
index 44a4332..02fcd70 100644
--- a/src/renderer/components/Balance.css
+++ b/src/renderer/components/Balance.module.scss
@@ -6,6 +6,10 @@
display: inline-block;
padding: 0 .3rem;
border-radius: 4px;
+
+ :global(.number-unit--main) & {
+ font-size: 2.5rem;
+ }
}
.updated {
diff --git a/src/renderer/components/Spinner.css b/src/renderer/components/Spinner.css
deleted file mode 100644
index 640c4a6..0000000
--- a/src/renderer/components/Spinner.css
+++ /dev/null
@@ -1,28 +0,0 @@
-.spinner {
- position: relative;
- text-align: center;
- margin: auto;
- width: 100%;
-}
-
-.spinner::before {
- content: '';
- box-sizing: border-box;
- position: absolute;
- top: 0;
- left: 50%;
- width: 20px;
- height: 20px;
- margin-top: -20px;
- margin-left: -10px;
- border-radius: 50%;
- border: 2px solid #7b1173;
- border-top-color: #e000cf;
- animation: spinner .6s linear infinite;
-}
-
-@keyframes spinner {
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/src/renderer/components/Spinner.jsx b/src/renderer/components/Spinner.jsx
index a0c8cce..14e53de 100644
--- a/src/renderer/components/Spinner.jsx
+++ b/src/renderer/components/Spinner.jsx
@@ -1,5 +1,6 @@
import React from 'react'
-import './Spinner.css'
-const Spinner = () =>
+import styles from './Spinner.module.scss'
+
+const Spinner = () =>
export default Spinner
diff --git a/src/renderer/components/Spinner.module.scss b/src/renderer/components/Spinner.module.scss
new file mode 100644
index 0000000..b01c1a0
--- /dev/null
+++ b/src/renderer/components/Spinner.module.scss
@@ -0,0 +1,28 @@
+.spinner {
+ position: relative;
+ text-align: center;
+ margin: auto;
+ width: 100%;
+
+ &::before {
+ content: '';
+ box-sizing: border-box;
+ position: absolute;
+ top: 0;
+ left: 50%;
+ width: 20px;
+ height: 20px;
+ margin-top: -20px;
+ margin-left: -10px;
+ border-radius: 50%;
+ border: 2px solid #7b1173;
+ border-top-color: #e000cf;
+ animation: spinner .6s linear infinite;
+ }
+}
+
+@keyframes spinner {
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/renderer/components/Titlebar.css b/src/renderer/components/Titlebar.css
deleted file mode 100644
index 9c58888..0000000
--- a/src/renderer/components/Titlebar.css
+++ /dev/null
@@ -1,48 +0,0 @@
-.titlebar {
- position: fixed;
- width: 100%;
- height: 35px;
- line-height: 35px;
- text-align: center;
- user-select: none;
- background: linear-gradient(to top, #ccc 0%, #d6d6d6 1px, #ebebeb 100%);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5);
- transition: opacity .15s ease-out;
-}
-
-.dark .titlebar {
- background: linear-gradient(to top, #141416 0%, #38383c 1px, #3f3f44 100%);
- box-shadow: none;
-}
-
-.fullscreen .titlebar {
- opacity: 0;
-}
-
-.blur .titlebar {
- background: #f6f6f6;
-}
-
-.blur.dark .titlebar {
- background: linear-gradient(to top, #141416 0%, #2d2a32 1px, #2d2a32 100%);
-}
-
-.header-title {
- line-height: 35px;
- height: 35px;
- font-weight: 400;
- font-size: 13px;
- color: #555;
-}
-
-.dark .header-title {
- color: #b6b3ba;
-}
-
-.blur .header-title {
- color: #b6b6b6;
-}
-
-.blur.dark .header-title {
- color: #67666e;
-}
diff --git a/src/renderer/components/Titlebar.jsx b/src/renderer/components/Titlebar.jsx
index fd813ce..fc1daa0 100644
--- a/src/renderer/components/Titlebar.jsx
+++ b/src/renderer/components/Titlebar.jsx
@@ -1,10 +1,10 @@
-import './Titlebar.css'
import React from 'react'
import pkg from '../../../package.json'
+import styles from './Titlebar.module.scss'
const Titlebar = () => (
-
- {pkg.productName}
+
)
diff --git a/src/renderer/components/Titlebar.module.scss b/src/renderer/components/Titlebar.module.scss
new file mode 100644
index 0000000..d55a871
--- /dev/null
+++ b/src/renderer/components/Titlebar.module.scss
@@ -0,0 +1,58 @@
+.titlebar {
+ position: fixed;
+ width: 100%;
+ height: 35px;
+ line-height: 35px;
+ text-align: center;
+ user-select: none;
+ background: linear-gradient(to top, #ccc 0%, #d6d6d6 1px, #ebebeb 100%);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5);
+ transition: opacity .15s ease-out;
+
+ :global(.dark) & {
+ background: linear-gradient(
+ to top,
+ #141416 0%,
+ #38383c 1px,
+ #3f3f44 100%
+ );
+ box-shadow: none;
+ }
+
+ :global(.fullscreen) & {
+ opacity: 0;
+ }
+
+ :global(.blur) & {
+ background: #f6f6f6;
+ }
+
+ :global(.blur.dark) & {
+ background: linear-gradient(
+ to top,
+ #141416 0%,
+ #2d2a32 1px,
+ #2d2a32 100%
+ );
+ }
+}
+
+.titlebarTitle {
+ line-height: 35px;
+ height: 35px;
+ font-weight: 400;
+ font-size: 13px;
+ color: #555;
+
+ :global(.dark) & {
+ color: #b6b3ba;
+ }
+
+ :global(.blur) & {
+ color: #b6b6b6;
+ }
+
+ :global(.blur.dark) & {
+ color: #67666e;
+ }
+}
diff --git a/src/renderer/components/Welcome.css b/src/renderer/components/Welcome.css
deleted file mode 100644
index 3d23765..0000000
--- a/src/renderer/components/Welcome.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.welcome {
- width: 100%;
- height: 100%;
- text-align: center;
- margin-top: 2rem;
-}
-
-.welcome svg {
- display: block;
- width: 5rem;
- height: 5rem;
- margin: 0 auto 2rem auto;
- fill: #e2e2e2;
-}
-
-.dark .welcome svg {
- fill: #41474e;
-}
diff --git a/src/renderer/components/Welcome.jsx b/src/renderer/components/Welcome.jsx
index cc7a335..0f8e937 100644
--- a/src/renderer/components/Welcome.jsx
+++ b/src/renderer/components/Welcome.jsx
@@ -1,11 +1,11 @@
import React from 'react'
import { Link } from '@reach/router'
import IconRocket from '../images/rocket.svg'
-import './Welcome.css'
+import styles from './Welcome.module.scss'
import { AppContext } from '../store/createContext'
const Welcome = () => (
-
+
{context => (
diff --git a/src/renderer/components/Welcome.module.scss b/src/renderer/components/Welcome.module.scss
new file mode 100644
index 0000000..c389e27
--- /dev/null
+++ b/src/renderer/components/Welcome.module.scss
@@ -0,0 +1,18 @@
+.welcome {
+ width: 100%;
+ height: 100%;
+ text-align: center;
+ margin-top: 2rem;
+
+ svg {
+ display: block;
+ width: 5rem;
+ height: 5rem;
+ margin: 0 auto 2rem auto;
+ fill: #e2e2e2;
+ }
+
+ :global(.dark) & svg {
+ fill: #41474e;
+ }
+}
diff --git a/src/renderer/screens/Home.css b/src/renderer/screens/Home.css
index d43d30b..71dbabb 100644
--- a/src/renderer/screens/Home.css
+++ b/src/renderer/screens/Home.css
@@ -59,7 +59,3 @@
.dark .number-unit--main {
border-bottom-color: #303030;
}
-
-.number-unit--main .number {
- font-size: 2.5rem;
-}
diff --git a/webpack.common.config.js b/webpack.common.config.js
index a6e3bdc..e4ff182 100644
--- a/webpack.common.config.js
+++ b/webpack.common.config.js
@@ -1,27 +1,67 @@
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const defaultInclude = [path.resolve(__dirname, 'src', 'renderer')]
+const isDevelopment = process.env.NODE_ENV !== 'production'
+
module.exports = {
+ mode: isDevelopment ? 'development' : 'production',
entry: path.resolve(__dirname, 'src', 'renderer', 'index.js'),
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
- publicPath: './'
+ publicPath: isDevelopment ? '/' : './'
},
module: {
rules: [
+ {
+ test: /\.(js|jsx)?$/,
+ use: ['babel-loader'],
+ include: defaultInclude
+ },
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
include: defaultInclude
},
{
- test: /\.(js|jsx)?$/,
- use: ['babel-loader'],
- include: defaultInclude
+ test: /\.module\.(scss|sass)$/,
+ include: defaultInclude,
+ loader: [
+ isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
+ {
+ loader: 'css-loader',
+ options: {
+ modules: true,
+ localIdentName: '[name]__[local]___[hash:base64:5]',
+ camelCase: true,
+ sourceMap: isDevelopment
+ }
+ },
+ {
+ loader: 'sass-loader',
+ options: {
+ sourceMap: isDevelopment
+ }
+ }
+ ]
+ },
+ {
+ test: /\.(scss|sass)$/,
+ exclude: /\.module.(scss|sass)$/,
+ loader: [
+ isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
+ 'css-loader',
+ {
+ loader: 'sass-loader',
+ options: {
+ sourceMap: isDevelopment
+ }
+ }
+ ]
},
{
test: /\.(jpe?g|png|gif)$/,
@@ -36,11 +76,15 @@ module.exports = {
]
},
resolve: {
- extensions: ['*', '.js', '.jsx']
+ extensions: ['*', '.js', '.jsx', '.scss']
},
target: 'electron-renderer',
plugins: [
new HtmlWebpackPlugin(),
+ new MiniCssExtractPlugin({
+ filename: isDevelopment ? '[name].css' : '[name].[hash].css',
+ chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
+ }),
new CopyPlugin([
{ from: './src/renderer/images/icon.*', to: './', flatten: true }
])
diff --git a/webpack.dev.config.js b/webpack.dev.config.js
index bd1fe60..d3025d7 100644
--- a/webpack.dev.config.js
+++ b/webpack.dev.config.js
@@ -3,12 +3,6 @@ const common = require('./webpack.common.config')
const { spawn } = require('child_process')
module.exports = Object.assign({}, common, {
- mode: 'development',
- output: {
- path: path.resolve(__dirname, 'build'),
- filename: 'bundle.js',
- publicPath: '/'
- },
devtool: 'cheap-source-map',
devServer: {
contentBase: path.resolve(__dirname, 'build'),