Permalink
Browse files

Styles and improved visualization

This commit adds some CSS styles and tooling around them. Autoprefixer
is used to add vendor prefixing.

Instead of adding the dependencies to package.json manually, you can
install them from the command line:

npm install --save classnames
npm install --save-dev css-loader style-loader autoprefixer-loader
1 parent 235ccee commit 33262cecf994de736862354914f53a2cbb03ddf8 @teropa committed Sep 6, 2015
Showing with 150 additions and 0 deletions.
  1. +6 −0 dist/index.html
  2. +4 −0 package.json
  3. +10 −0 src/components/Results.jsx
  4. +2 −0 src/components/Vote.jsx
  5. +2 −0 src/index.jsx
  6. +123 −0 src/style.css
  7. +3 −0 webpack.config.js
View
@@ -1,5 +1,11 @@
<!DOCTYPE html>
<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css"></link>
+ <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400,700"></link>
+</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
View
@@ -9,17 +9,21 @@
"author": "",
"license": "ISC",
"devDependencies": {
+ "autoprefixer-loader": "^2.1.0",
"babel-core": "^5.8.23",
"babel-loader": "^5.3.2",
"chai": "^3.2.0",
"chai-immutable": "^1.3.0",
+ "css-loader": "^0.17.0",
"jsdom": "^3.1.2",
"mocha": "^2.3.0",
"react-hot-loader": "^1.3.0",
+ "style-loader": "^0.12.3",
"webpack": "^1.12.0",
"webpack-dev-server": "^1.10.1"
},
"dependencies": {
+ "classnames": "^2.1.3",
"immutable": "^3.7.5",
"react": "^0.13.3",
"react-redux": "^2.1.0",
@@ -3,6 +3,8 @@ import {connect} from 'react-redux';
import Winner from './Winner';
import * as actionCreators from '../action_creators';
+export const VOTE_WIDTH_PERCENT = 8;
+
export const Results = React.createClass({
mixins: [React.addons.PureRenderMixin],
getPair: function() {
@@ -14,6 +16,9 @@ export const Results = React.createClass({
}
return 0;
},
+ getVotesBlockWidth: function(entry) {
+ return (this.getVotes(entry) * VOTE_WIDTH_PERCENT) + '%';
+ },
render: function() {
return this.props.winner ?
<Winner ref="winner" winner={this.props.winner} /> :
@@ -22,6 +27,11 @@ export const Results = React.createClass({
{this.getPair().map(entry =>
<div key={entry} className="entry">
<h1>{entry}</h1>
+ <div className="voteVisualization">
+ <div className="votesBlock"
+ style={{width: this.getVotesBlockWidth(entry)}}>
+ </div>
+ </div>
<div className="voteCount">
{this.getVotes(entry)}
</div>
@@ -1,4 +1,5 @@
import React from 'react/addons';
+import classNames from 'classnames'
export default React.createClass({
mixins: [React.addons.PureRenderMixin],
@@ -15,6 +16,7 @@ export default React.createClass({
return <div className="voting">
{this.getPair().map(entry =>
<button key={entry}
+ className={classNames({voted: this.hasVotedFor(entry)})}
disabled={this.isDisabled()}
onClick={() => this.props.vote(entry)}>
<h1>{entry}</h1>
View
@@ -10,6 +10,8 @@ import App from './components/App';
import {VotingContainer} from './components/Voting';
import {ResultsContainer} from './components/Results';
+require('./style.css');
+
const socket = io(`${location.protocol}//${location.hostname}:8090`);
socket.on('state', state =>
store.dispatch(setState(state))
View
@@ -0,0 +1,123 @@
+body {
+ font-family: 'Open Sans', sans-serif;
+ background-color: #673AB7;
+ color: white;
+}
+
+/* Voting Screen */
+
+.voting {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ display: flex;
+ flex-direction: column;
+
+ user-select: none;
+}
+
+.voting button {
+ flex: 1 0 0;
+
+ background-color: #673AB7;
+ border-width: 0;
+}
+.voting button:first-child {
+ border-bottom: 1px solid white;
+}
+.voting button:active {
+ background-color: white;
+ color: #311B92;
+}
+.voting button.voted {
+ background-color: #311B92;
+}
+.voting button:not(.voted) .label {
+ visibility: hidden;
+}
+.voting button .label {
+ opacity: 0.87;
+}
+.voting button.votedAgainst * {
+ opacity: 0.3;
+}
+
+@media only screen and (min-device-width: 500px) {
+ .voting {
+ flex-direction: row;
+ }
+ .voting button:first-child {
+ border-bottom-width: 0;
+ border-right: 1px solid white;
+ }
+}
+
+/* Results Screen */
+
+.results {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ display: flex;
+ flex-direction: column;
+}
+.results .tally {
+ flex: 1;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+.results .tally .entry {
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+}
+
+.results .tally h1 {
+ width: 25%;
+}
+.results .tally .voteVisualization {
+ height: 50px;
+ width: 50%;
+ display: flex;
+ justify-content: flex-start;
+
+ background-color: #7E57C2;
+}
+.results .tally .votesBlock {
+ background-color: white;
+ transition: width 0.5s;
+}
+.results .tally .voteCount {
+ font-size: 2rem;
+}
+
+.results .management {
+ display: flex;
+
+ height: 2em;
+ border-top: 1px solid #aaa;
+}
+
+.results .management button {
+ border: 0;
+ background-color: black;
+ color: #aaa;
+}
+.results .management .next {
+ flex: 1;
+}
+
+/* Winner View */
+
+.winner {
+ font-size: 4rem;
+ text-align: center;
+}
View
@@ -11,6 +11,9 @@ module.exports = {
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'react-hot!babel'
+ }, {
+ test: /\.css$/,
+ loader: 'style!css!autoprefixer?browsers=last 2 versions'
}]
},
resolve: {

0 comments on commit 33262ce

Please sign in to comment.