Add px/pt toggle (#67)

diff --git a/src/actions/app.js b/src/actions/app.js
index 3436ae4..9310b47 100644
--- a/src/actions/app.js
+++ b/src/actions/app.js
@@ -2,6 +2,7 @@
 export const BLUR_TEXT_COLOR = 'BLUR_TEXT_COLOR';
 export const UPDATE_FONT_SIZE = 'UPDATE_FONT_SIZE';
 export const BLUR_FONT_SIZE = 'BLUR_FONT_SIZE';
+export const UPDATE_FONT_SIZE_UNIT = 'UPDATE_FONT_SIZE_UNIT';
 export const TOGGLE_FONT_WEIGHT = 'TOGGLE_FONT_WEIGHT';
 export const UPDATE_BACKGROUND_COLOR = 'UPDATE_BACKGROUND_COLOR';
 export const BLUR_BACKGROUND_COLOR = 'BLUR_BACKGROUND_COLOR';
@@ -44,6 +45,13 @@
   };
 }
 
+export function updateFontSizeUnit(value) {
+  return {
+    type: UPDATE_FONT_SIZE_UNIT,
+    value
+  };
+}
+
 export function toggleFontWeight() {
   return {
     type: TOGGLE_FONT_WEIGHT
diff --git a/src/components/App/App.js b/src/components/App/App.js
index 58f2b6c..170a274 100644
--- a/src/components/App/App.js
+++ b/src/components/App/App.js
@@ -15,6 +15,7 @@
   return {
     textColor: state.textColor,
     fontSize: state.fontSize,
+    fontSizeUnit: state.fontSizeUnit,
     isFontBold: state.isFontBold,
     backgroundColor: state.backgroundColor,
     accessibilityLevel: state.accessibilityLevel
@@ -22,14 +23,14 @@
 }
 
 function App(props) {
-  const { textColor, fontSize, isFontBold,
+  const { textColor, fontSize, fontSizeUnit, isFontBold,
           backgroundColor, accessibilityLevel } = props;
   const isUserInputValid = textColor.isValueValid &&
                            backgroundColor.isValueValid &&
                            fontSize.isValid;
   const fontSizeValue = parseInt(fontSize.value, 10);
   const accessibleContrastRatio = isUserInputValid ?
-    accessibleContrast(accessibilityLevel, fontSizeValue, isFontBold) : null;
+    accessibleContrast(accessibilityLevel, fontSizeValue, fontSizeUnit, isFontBold) : null;
   const isAccessible = isUserInputValid ?
     (contrast(textColor.value, backgroundColor.value) >= accessibleContrastRatio) : null;
 
@@ -57,6 +58,7 @@
 App.propTypes = {
   textColor: PropTypes.object.isRequired,
   fontSize: PropTypes.object.isRequired,
+  fontSizeUnit: PropTypes.string.isRequired,
   isFontBold: PropTypes.bool.isRequired,
   backgroundColor: PropTypes.object.isRequired,
   accessibilityLevel: PropTypes.string.isRequired
diff --git a/src/components/App/components/Preview/Preview.js b/src/components/App/components/Preview/Preview.js
index a63e8ec..bae3cbe 100644
--- a/src/components/App/components/Preview/Preview.js
+++ b/src/components/App/components/Preview/Preview.js
@@ -2,47 +2,57 @@
 
 import React, { PropTypes } from 'react';
 import { connect } from 'react-redux';
-import { MAX_FONT_SIZE } from 'constants';
 import { str2sixDigitHex, contrast, findClosestAccessibleColor } from 'utils/color/color';
 import MultilineEllipsis from 'MultilineEllipsis/MultilineEllipsis';
+import { fontSizeInPx } from 'utils/accessibility/accessibility';
 
 const loremIpsum = `
-  Lorem ipsum dolor sit amet, ut pri essent facilis constituto, etiam assueverit
-  signiferumque ex ius. Quas quaestio ea duo. Purto magna aperiam no pri. Pri
-  prompta partiendo efficiendi ne, sed tritani deterruisset necessitatibus id,
-  ad est sint noluisse.
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
 `;
 
 function calcContrast(color1, color2) {
   return Math.round(100 * contrast(color1, color2)) / 100;
 }
 
-function calcLinesToShow(fontSize) {
-  if (fontSize <= 12) {
+function calcLinesToShow(fontSizeInPx) {
+  if (fontSizeInPx <= 8) {
+    return 7;
+  }
+
+  if (fontSizeInPx <= 10) {
+    return 6;
+  }
+
+  if (fontSizeInPx <= 12) {
     return 5;
   }
 
-  if (fontSize <= 16) {
+  if (fontSizeInPx <= 16) {
     return 4;
   }
 
-  return 3;
+  if (fontSizeInPx <= 21) {
+    return 3;
+  }
+
+  return 2;
 }
 
 function mapStateToProps(state) {
   return {
     textColor: state.textColor,
     fontSize: state.fontSize,
+    fontSizeUnit: state.fontSizeUnit,
     isFontBold: state.isFontBold,
     backgroundColor: state.backgroundColor
   };
 }
 
 function Preview(props) {
-  const { textColor, fontSize, isFontBold, backgroundColor,
+  const { textColor, fontSize, fontSizeUnit, isFontBold, backgroundColor,
           accessibilityLevel, accessibleContrast, isAccessible } = props;
   const previewContentStyle = {
-    fontSize: Math.min(parseInt(fontSize.value, 10), MAX_FONT_SIZE),
+    fontSize: fontSizeInPx(parseInt(fontSize.value, 10),fontSizeUnit),
     fontWeight: isFontBold ? '500' : '300'
   };
   const originalTextColor = str2sixDigitHex(textColor.value);
@@ -173,9 +183,9 @@
 Preview.propTypes = {
   textColor: PropTypes.object.isRequired,
   fontSize: PropTypes.object.isRequired,
+  fontSizeUnit: PropTypes.string.isRequired,
   isFontBold: PropTypes.bool.isRequired,
   backgroundColor: PropTypes.object.isRequired,
-
   accessibilityLevel: PropTypes.string.isRequired,
   accessibleContrast: PropTypes.number.isRequired,
   isAccessible: PropTypes.bool.isRequired
diff --git a/src/components/App/components/UserInput/UserInput.js b/src/components/App/components/UserInput/UserInput.js
index bafc65a..602bdd9 100644
--- a/src/components/App/components/UserInput/UserInput.js
+++ b/src/components/App/components/UserInput/UserInput.js
@@ -4,7 +4,7 @@
 import { connect } from 'react-redux';
 import { MIN_FONT_SIZE, MAX_FONT_SIZE } from 'constants';
 import { updateTextColor, blurTextColor, updateFontSize, blurFontSize,
-         toggleFontWeight, updateBackgroundColor, blurBackgroundColor,
+         updateFontSizeUnit, toggleFontWeight, updateBackgroundColor, blurBackgroundColor,
          updateAccessibilityLevel } from 'actions/app';
 import Editable from 'Editable/Editable';
 import Toggle from 'Toggle/Toggle';
@@ -13,6 +13,7 @@
   return {
     textColor: state.textColor,
     fontSize: state.fontSize,
+    fontSizeUnit: state.fontSizeUnit,
     isFontBold: state.isFontBold,
     backgroundColor: state.backgroundColor,
     accessibilityLevel: state.accessibilityLevel
@@ -25,6 +26,7 @@
     blurTextColor: () => dispatch(blurTextColor()),
     updateFontSize: value => dispatch(updateFontSize(value)),
     blurFontSize: () => dispatch(blurFontSize()),
+    updateFontSizeUnit: value => dispatch(updateFontSizeUnit(value)),
     toggleFontWeight: () => dispatch(toggleFontWeight()),
     updateBackgroundColor: value => dispatch(updateBackgroundColor('value', value)),
     blurBackgroundColor: () => dispatch(blurBackgroundColor()),
@@ -33,10 +35,10 @@
 }
 
 function UserInput(props) {
-  const { textColor, fontSize, isFontBold, backgroundColor,
+  const { textColor, fontSize, fontSizeUnit, isFontBold, backgroundColor,
           accessibilityLevel, updateTextColor,
           blurTextColor, updateFontSize, blurFontSize,
-          toggleFontWeight, updateBackgroundColor, blurBackgroundColor,
+          updateFontSizeUnit, toggleFontWeight, updateBackgroundColor, blurBackgroundColor,
           updateAccessibilityLevel } = props;
 
   return (
@@ -76,7 +78,14 @@
                 }}
               />
             </span>
-            pt and
+            <span className={styles.fontSizeUnitContainer}>
+              <Toggle
+                values={['pt', 'px']}
+                currentValue={fontSizeUnit}
+                onChange={updateFontSizeUnit}
+              />
+            </span>
+            and
             <span className={styles.fontWeightContainer}>
               <Toggle
                 values={['regular', 'bold']}
@@ -136,6 +145,7 @@
 UserInput.propTypes = {
   textColor: PropTypes.object.isRequired,
   fontSize: PropTypes.object.isRequired,
+  fontSizeUnit: PropTypes.string.isRequired,
   isFontBold: PropTypes.bool.isRequired,
   backgroundColor: PropTypes.object.isRequired,
   accessibilityLevel: PropTypes.string.isRequired,
@@ -144,6 +154,7 @@
   blurTextColor: PropTypes.func.isRequired,
   updateFontSize: PropTypes.func.isRequired,
   blurFontSize: PropTypes.func.isRequired,
+  updateFontSizeUnit: PropTypes.func.isRequired,
   toggleFontWeight: PropTypes.func.isRequired,
   updateBackgroundColor: PropTypes.func.isRequired,
   blurBackgroundColor: PropTypes.func.isRequired,
diff --git a/src/components/App/components/UserInput/UserInput.less b/src/components/App/components/UserInput/UserInput.less
index cd21c56..70f2def 100644
--- a/src/components/App/components/UserInput/UserInput.less
+++ b/src/components/App/components/UserInput/UserInput.less
@@ -79,6 +79,11 @@
   }
 }
 
+.fontSizeUnitContainer {
+  composes: paragraph;
+  white-space: nowrap;
+}
+
 .backgroundColorContainer {
   composes: paragraph;
 
diff --git a/src/constants.js b/src/constants.js
index 3b86956..dc0552e 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -1,5 +1,5 @@
 export const DOMAIN = 'accessible-colors.com';
 export const SITE_URL = `http://${DOMAIN}`;
 export const REPO = 'moroshko/accessible-colors';
-export const MIN_FONT_SIZE = 10;
-export const MAX_FONT_SIZE = 21;
+export const MIN_FONT_SIZE = 8; // Min font size input value, regardless of fontSizeUnit
+export const MAX_FONT_SIZE = 22; // Max font size input value, regardless of fontSizeUnit
diff --git a/src/reducers/app.js b/src/reducers/app.js
index 2c1bb82..734a4c5 100644
--- a/src/reducers/app.js
+++ b/src/reducers/app.js
@@ -3,7 +3,7 @@
 import { isIntegerInRange, correctInteger } from 'utils/number/number';
 import { UPDATE_TEXT_COLOR, BLUR_TEXT_COLOR,
          UPDATE_FONT_SIZE, BLUR_FONT_SIZE,
-         TOGGLE_FONT_WEIGHT,
+         UPDATE_FONT_SIZE_UNIT, TOGGLE_FONT_WEIGHT,
          UPDATE_BACKGROUND_COLOR, BLUR_BACKGROUND_COLOR,
          UPDATE_ACCESSIBILITY_LEVEL,
          LOAD_GITHUB_STARS_SUCCESS, LOAD_TWITTER_COUNT_SUCCESS } from 'actions/app';
@@ -15,7 +15,7 @@
 const initialBackgroundColorHSL = str2hsl(initialBackgroundColor);
 const initialTextColorHSL = str2hsl(initialTextColor);
 const initialState = {
-  githubStars: '31',
+  githubStars: '53',
   twitterCount: '0',
   textColor: {
     isValueValid: true,
@@ -29,8 +29,9 @@
   },
   fontSize: {
     isValid: true,
-    value: '14'
+    value: '18'
   },
+  fontSizeUnit: 'px',
   isFontBold: false,
   backgroundColor: {
     isValueValid: true,
@@ -83,6 +84,12 @@
         }
       } : state;
 
+    case UPDATE_FONT_SIZE_UNIT:
+      return {
+        ...state,
+        fontSizeUnit: action.value
+      };
+
     case TOGGLE_FONT_WEIGHT:
       return {
         ...state,
diff --git a/src/utils/accessibility/accessibility.js b/src/utils/accessibility/accessibility.js
index 5595a04..4880363 100644
--- a/src/utils/accessibility/accessibility.js
+++ b/src/utils/accessibility/accessibility.js
@@ -1,22 +1,36 @@
+// 1pt = 1.333px according to http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#visual-audio-contrast-contrast-73-head
+const PIXELS_IN_POINT = 1.333;
+
 // http://www.w3.org/TR/2008/REC-WCAG20-20081211/#larger-scaledef
-function isLargeScale(fontSize, isFontBold) {
-  return isFontBold ? (fontSize >= 14) : (fontSize >= 18);
+function isLargeScale(fontSize, fontSizeUnit, isFontBold) {
+  const points = fontSizeInPt(fontSize, fontSizeUnit);
+
+  return isFontBold ? (points >= 14) : (points >= 18);
 }
 
-function accessibleContrast(accessibilityLevel, fontSize, isFontBold) {
+function fontSizeInPt(fontSize, fontSizeUnit) {
+  return (fontSizeUnit === 'pt') ? fontSize : Math.round(fontSize / PIXELS_IN_POINT);
+}
+
+function fontSizeInPx(fontSize, fontSizeUnit) {
+  return (fontSizeUnit === 'px') ? fontSize : Math.round(fontSize * PIXELS_IN_POINT);
+}
+
+function accessibleContrast(accessibilityLevel, fontSize, fontSizeUnit, isFontBold) {
   switch (accessibilityLevel) {
     // http://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-contrast
     case 'AA':
-      return isLargeScale(fontSize, isFontBold) ? 3 : 4.5;
+      return isLargeScale(fontSize, fontSizeUnit, isFontBold) ? 3 : 4.5;
 
     // http://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast7
     case 'AAA':
-      return isLargeScale(fontSize, isFontBold) ? 4.5 : 7;
+      return isLargeScale(fontSize, fontSizeUnit, isFontBold) ? 4.5 : 7;
   }
 
   return null;
 }
 
 export {
-  accessibleContrast
+  accessibleContrast,
+  fontSizeInPx
 };
diff --git a/src/utils/accessibility/accessibility.test.js b/src/utils/accessibility/accessibility.test.js
index 92a2f57..2a8aa01 100644
--- a/src/utils/accessibility/accessibility.test.js
+++ b/src/utils/accessibility/accessibility.test.js
@@ -1,30 +1,52 @@
 import { expect } from 'chai';
-import { accessibleContrast } from './accessibility';
+import { accessibleContrast, fontSizeInPx } from './accessibility';
 
 describe('Accessibility Utils', () => {
   describe('accessibleContrast()', () => {
     describe('AA', () => {
       it('should be 4.5 for normal scale', () => {
-        expect(accessibleContrast('AA', 12, false)).to.equal(4.5);
-        expect(accessibleContrast('AA', 17, false)).to.equal(4.5);
+        expect(accessibleContrast('AA', 12, 'pt', false)).to.equal(4.5);
+        expect(accessibleContrast('AA', 17, 'pt', false)).to.equal(4.5);
+        expect(accessibleContrast('AA', 14, 'px', false)).to.equal(4.5);
+        expect(accessibleContrast('AA', 23, 'px', false)).to.equal(4.5);
       });
 
       it('should be 3 for large scale', () => {
-        expect(accessibleContrast('AA', 14, true)).to.equal(3);
-        expect(accessibleContrast('AA', 18, false)).to.equal(3);
+        expect(accessibleContrast('AA', 14, 'pt', true)).to.equal(3);
+        expect(accessibleContrast('AA', 18, 'pt', false)).to.equal(3);
+        expect(accessibleContrast('AA', 18, 'pt', true)).to.equal(3);
+        expect(accessibleContrast('AA', 24, 'pt', false)).to.equal(3);
       });
     });
 
     describe('AAA', () => {
       it('should be 7 for normal scale', () => {
-        expect(accessibleContrast('AAA', 12, false)).to.equal(7);
-        expect(accessibleContrast('AAA', 17, false)).to.equal(7);
+        expect(accessibleContrast('AAA', 12, 'pt', false)).to.equal(7);
+        expect(accessibleContrast('AAA', 17, 'pt', false)).to.equal(7);
+        expect(accessibleContrast('AAA', 14, 'px', false)).to.equal(7);
+        expect(accessibleContrast('AAA', 23, 'px', false)).to.equal(7);
       });
 
       it('should be 4.5 for large scale', () => {
-        expect(accessibleContrast('AAA', 14, true)).to.equal(4.5);
-        expect(accessibleContrast('AAA', 18, false)).to.equal(4.5);
+        expect(accessibleContrast('AAA', 14, 'pt', true)).to.equal(4.5);
+        expect(accessibleContrast('AAA', 18, 'pt', false)).to.equal(4.5);
+        expect(accessibleContrast('AAA', 18, 'px', true)).to.equal(4.5);
+        expect(accessibleContrast('AAA', 24, 'px', false)).to.equal(4.5);
       });
     });
   });
+
+  describe('fontSizeInPx()', () => {
+    it('Should round up', () => {
+      expect(fontSizeInPx(14, 'pt')).to.equal(19);
+    });
+
+    it('Should round down', () => {
+      expect(fontSizeInPx(10, 'pt')).to.equal(13);
+    });
+
+    it('Should return number unchanged', () => {
+      expect(fontSizeInPx(10, 'px')).to.equal(10);
+    });
+  });
 });
diff --git a/src/variables.less b/src/variables.less
index 6a68886..5d1b76b 100644
--- a/src/variables.less
+++ b/src/variables.less
@@ -1,5 +1,5 @@
 @small-screen-width: 320px;
-@medium-screen-width: 510px;
+@medium-screen-width: 520px;
 @large-screen-width: 768px;
 
 @small: ~"(max-width:" (@medium-screen-width - 1px) ~")";