diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 00000000000..7831750b738
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,91 @@
+# Ruby CircleCI 2.0 configuration file
+#
+# Check https://circleci.com/docs/2.0/language-ruby/ for more details
+#
+version: 2
+jobs:
+ build:
+ docker:
+ # Specify the Ruby version you desire here
+ - image: circleci/ruby:2.3.3-node-browsers
+ environment:
+ RAILS_ENV: test
+ CC_TEST_REPORTER_ID: faecd27e9aed532634b3f4d3e251542d7de9457cfca96a94208a63270ef9b42e
+ COVERAGE: true
+
+ # Specify service dependencies here if necessary
+ # CircleCI maintains a library of pre-built images
+ # documented at https://circleci.com/docs/2.0/circleci-images/
+ - image: circleci/postgres:9.4.12-alpine
+ environment:
+ POSTGRES_USER: circleci
+
+ - image: redis:4.0.1
+
+ working_directory: ~/identity-idp
+
+ steps:
+ - checkout
+
+ - restore-cache:
+ key: identity-idp-{{ checksum "Gemfile.lock" }}
+
+ - run:
+ name: Install dependencies
+ command: |
+ gem install bundler
+ bundle install --deployment --jobs=4 --retry=3 --without deploy development doc production --path vendor/bundle
+ - run:
+ name: Install phantomjs
+ command: |
+ sudo curl --output /tmp/phantomjs https://s3.amazonaws.com/circle-downloads/phantomjs-2.1.1
+ sudo chmod ugo+x /tmp/phantomjs
+ sudo ln -sf /tmp/phantomjs /usr/local/bin/phantomjs
+ - run:
+ name: Install Code Climate Test Reporter
+ command: |
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
+ chmod +x ./cc-test-reporter
+
+ # Store bundle cache
+ - save-cache:
+ key: identity-idp-{{ checksum "Gemfile.lock" }}
+ paths:
+ - vendor/bundle
+
+ - run:
+ name: Test Setup
+ command: |
+ npm install
+ npm run build
+ cp config/application.yml.example config/application.yml
+ cp certs/saml.crt.example certs/saml.crt
+ cp keys/saml.key.enc.example keys/saml.key.enc
+ cp keys/equifax_rsa.example keys/equifax_rsa
+ gpg --dearmor < keys/equifax_gpg.pub.example > keys/equifax_gpg.pub.bin
+ gpg --import keys/equifax_gpg.example
+ bundle exec rake db:setup --trace
+ bundle exec rake assets:precompile
+
+ - run:
+ name: Run Tests
+ command: |
+ mkdir /tmp/test-results
+ ./cc-test-reporter before-build
+
+ bundle exec rspec --format progress
+ bundle exec teaspoon
+ bundle exec slim-lint app/views
+
+ - run:
+ name: Upload Test Results to Code Climate
+ command: |
+ ./cc-test-reporter format-coverage -t simplecov $CIRCLE_ARTIFACTS/coverage/.resultset.json
+ ./cc-test-reporter upload-coverage
+
+ # collect reports
+ - store_test_results:
+ path: /tmp/test-results
+ - store_artifacts:
+ path: /tmp/test-results
+ destination: test-results
diff --git a/.gitignore b/.gitignore
index b18320e46b5..aebd7c9ecef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,17 +50,11 @@ Vagrantfile
/kitchen/cookbooks
/log/*
/private_certs/*
-/public/*.ico
-/public/*.png
-/public/*.svg
-/public/browserconfig.xml
-/public/manifest.json
/public/system
/public/user_flows
/spec/tmp
/test
/tmp/*
-/vendor/assets/fonts
/vendor/bundle
/node_modules
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index a8d124fb2ba..00000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-addons:
- postgresql: 9.3
- code_climate:
- repo_token:
- secure: '0RG6L9Sfur37NAB4X9rZypo6tffcWQtTcTpuSi08SfLN9jgiG2nHArVvw0nfNcSg7EmJ27ZnQxQqRjXlG76ufndlmoxgVX6xs96VyDyCOEFzRecE+wziNis+B3ACZb5OHaoMpomzsYFA73nYoCnPkgKUPAQFReTIqW7IB2Z1t7auTZe6kYBkKP7yB37wAVD4HMUDwm0spT0U2ur2GzSi86EqFEXR4NfhzGu5o7Y/hzWPxyyz+CRrujXhdWZl1FNtMj0mUM0/zSEEgUuCRRClGkD31RECjDKQQrXUkgQ1b3O/Nbih/8EjDqs4udLGnBYbtoahZ0ogZ/SV5xj7n2umNBJeLLTZO/qhWyQA4YDPuAZCw61VK1jpyQE1uZKxawYkgXBScmTWCJeAwEqZ4V8z4H4W2y1ZgegAoBCYOhv2+Dq4d9IaBUaeH3ukhEKX9H38yF1DEkp2sXKfXOxyEgpr0g0IIII9YVYQ89WExNmnZnfgd6lBqMFlP4wj8pQkCAeMG8+e+O0QsewzwlVgDDp5pQCgHEYq5X3quvWv0bRJDBL68/4fU9kEjv+RAo8Wx99x3nrZSzJglBrSaPt2/+eoVf3VTIJX4p8oO3aC/gcpbrWZrsfcGfLgKCEiu/ggzhicYhWY+Aa+pdeOefrO51ki9BIu84PoMrP2I7EREp2s1vw='
- apt:
- packages:
- - jq
-cache:
- directories:
- - "travis_phantomjs"
-before_install:
- - . $HOME/.nvm/nvm.sh
- - nvm install stable
- - nvm use stable
- - npm install
- - npm run build
- # Install PhantomJS 2.1.1 manually
- - "export PHANTOMJS_VERSION=2.1.1"
- - "phantomjs --version"
- - "export PATH=$PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin:$PATH"
- - "phantomjs --version"
- - "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs; fi"
- - "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then wget https://github.com/Medium/phantomjs/releases/download/v$PHANTOMJS_VERSION/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2; fi"
- - "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi"
- - "phantomjs --version"
-before_script:
- - cp config/application.yml.example config/application.yml
- - cp certs/saml.crt.example certs/saml.crt
- - cp keys/saml.key.enc.example keys/saml.key.enc
- - cp keys/equifax_rsa.example keys/equifax_rsa
- - gpg --dearmor < keys/equifax_gpg.pub.example > keys/equifax_gpg.pub.bin
- - gpg --import keys/equifax_gpg.example
- - bin/rake db:setup --trace
- - bin/rake assets:precompile
-bundler_args: "--deployment --jobs=3 --retry=3 --without deploy development doc production"
-cache: bundler
-language: ruby
-matrix:
- fast_finish: true
-notifications:
- email: false
-rvm:
- - ruby-2.3.3
-sudo: false
-script:
- - make test
- - bundle exec slim-lint app/views
- - bundle exec codeclimate-test-reporter
-services:
- - redis-server
-env:
- global:
- - COVERAGE=true
diff --git a/Gemfile b/Gemfile
index 00cf77b0301..eae7aba60bd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -30,7 +30,6 @@ gem 'phonelib'
gem 'phony_rails'
gem 'premailer-rails'
gem 'proofer', github: '18F/identity-proofer-gem', branch: 'master'
-gem 'rack-attack'
gem 'rack-cors', require: 'rack/cors'
gem 'readthis'
gem 'redis-session-store', github: '18F/redis-session-store', branch: 'master'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9ecc2a0fdc9..788f80f851e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,6 +1,6 @@
GIT
remote: git@github.com:18F/identity-equifax-api-client-gem.git
- revision: 889aad815bda2ff2a41cd2b108e2afae7f50d8b8
+ revision: 8021646f4a67216b0a1bdb99e501ad652104f889
branch: master
specs:
equifax (1.0.0)
@@ -712,7 +712,6 @@ DEPENDENCIES
premailer-rails
proofer!
pry-byebug
- rack-attack
rack-cors
rack-mini-profiler
rack-test
@@ -759,4 +758,4 @@ RUBY VERSION
ruby 2.3.3p222
BUNDLED WITH
- 1.15.3
+ 1.15.4
diff --git a/app/assets/images/no-verify.svg b/app/assets/images/no-verify.svg
new file mode 100644
index 00000000000..c302b772d27
--- /dev/null
+++ b/app/assets/images/no-verify.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/app/form-validation.js b/app/assets/javascripts/app/form-validation.js
index 7d534d6e6c0..33309692516 100644
--- a/app/assets/javascripts/app/form-validation.js
+++ b/app/assets/javascripts/app/form-validation.js
@@ -1,19 +1,35 @@
+import 'classlist.js';
+
const I18n = window.LoginGov.I18n;
document.addEventListener('DOMContentLoaded', () => {
- const form = document.querySelector('form');
- if (form) {
- const fields = ['dob', 'personal-key', 'ssn', 'zipcode'];
+ const forms = document.querySelectorAll('form');
+
+ function addListenerMulti(el, events, fn) {
+ events.split(' ').forEach(e => el.addEventListener(e, fn, false));
+ }
+
+ if (forms.length !== 0) {
+ [].forEach.call(forms, function(form) {
+ const inputs = form.querySelectorAll('.field');
+
+ if (inputs.length !== 0) {
+ [].forEach.call(inputs, function(input) {
+ const types = ['dob', 'personal-key', 'ssn', 'zipcode'];
+
+ addListenerMulti(input, 'input invalid', (e) => {
+ e.target.setCustomValidity('');
- fields.forEach(function(f) {
- const input = document.querySelector(`.${f}`);
- if (input) {
- input.addEventListener('input', () => {
- if (input.validity.patternMismatch) {
- input.setCustomValidity(I18n.t(`idv.errors.pattern_mismatch.${I18n.key(f)}`));
- } else {
- input.setCustomValidity('');
- }
+ if (e.target.validity.valueMissing) {
+ e.target.setCustomValidity(I18n.t('simple_form.required.text'));
+ } else if (e.target.validity.patternMismatch) {
+ types.forEach(function(type) {
+ if (e.target.classList.contains(type)) {
+ e.target.setCustomValidity(I18n.t(`idv.errors.pattern_mismatch.${I18n.key(type)}`));
+ }
+ });
+ }
+ });
});
}
});
diff --git a/app/assets/javascripts/misc/i18n-strings.js.erb b/app/assets/javascripts/misc/i18n-strings.js.erb
index a2bd235c4c7..3ae56e9768b 100644
--- a/app/assets/javascripts/misc/i18n-strings.js.erb
+++ b/app/assets/javascripts/misc/i18n-strings.js.erb
@@ -19,6 +19,7 @@ window.LoginGov = window.LoginGov || {};
'instructions.password.strength.iv',
'instructions.password.strength.v',
'links.remove',
+ 'simple_form.required.text',
'valid_email.validations.email.invalid',
'zxcvbn.feedback.a_word_by_itself_is_easy_to_guess',
'zxcvbn.feedback.add_another_word_or_two_uncommon_words_are_better',
diff --git a/app/assets/stylesheets/_vendor.scss b/app/assets/stylesheets/_vendor.scss
new file mode 100644
index 00000000000..241ef017389
--- /dev/null
+++ b/app/assets/stylesheets/_vendor.scss
@@ -0,0 +1,8 @@
+@import 'normalize.css/normalize';
+@import 'hint.css/hint';
+
+@import 'basscss-sass/basscss';
+@import 'basscss/margin';
+@import 'basscss/padding';
+@import 'basscss/responsive-margin';
+@import 'basscss/responsive-padding';
diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss
index 36142a61986..f47af41e55b 100644
--- a/app/assets/stylesheets/application.css.scss
+++ b/app/assets/stylesheets/application.css.scss
@@ -1,5 +1,6 @@
@import 'fonts';
-
-@import 'identity-style-guide/src/css/app';
-
+@import 'variables/colors';
+@import 'variables/app';
+@import 'vendor';
+@import 'components/all';
@import 'print';
diff --git a/app/assets/stylesheets/components/_abbr.scss b/app/assets/stylesheets/components/_abbr.scss
new file mode 100644
index 00000000000..6048d5e322b
--- /dev/null
+++ b/app/assets/stylesheets/components/_abbr.scss
@@ -0,0 +1,3 @@
+// normalize.css adds a text underline by default
+// scss-lint:disable QualifyingElement
+abbr[title] { text-decoration: none; }
diff --git a/app/assets/stylesheets/components/_accordion.scss b/app/assets/stylesheets/components/_accordion.scss
new file mode 100644
index 00000000000..3a59a7e044c
--- /dev/null
+++ b/app/assets/stylesheets/components/_accordion.scss
@@ -0,0 +1,87 @@
+.no-js {
+ .accordion {
+ .accordion-header {
+ cursor: initial;
+ }
+
+ .accordion-footer {
+ display: none;
+ }
+
+ .accordion-content {
+ display: block;
+ }
+
+ [class*="btn-"] {
+ display: none;
+ }
+ }
+}
+
+.accordion {
+ border: $border-width solid $border-color;
+ border-radius: $border-radius-md;
+
+ .accordion-header {
+ color: $blue;
+ cursor: pointer;
+ position: relative;
+
+ img {
+ position: absolute;
+ right: 1rem;
+ top: .8rem;
+ }
+ }
+
+ .accordion-content {
+ border-top: $border-width solid $border-color;
+ display: none;
+ opacity: 1;
+
+ &.shown {
+ display: block;
+ }
+ }
+
+ .accordion-footer {
+ background-color: $blue-lightest;
+ border-top: $border-width solid $border-color;
+ color: $blue;
+ cursor: pointer;
+ text-align: center;
+
+ img {
+ margin-top: -2px;
+ vertical-align: middle;
+ }
+ }
+}
+
+.animate-in {
+ animation: accordionIn .2s normal ease-in both 1;
+}
+
+.animate-out {
+ animation: accordionOut .15s normal ease-out both 1;
+}
+
+@keyframes accordionIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes accordionOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
diff --git a/app/assets/stylesheets/components/_alert.scss b/app/assets/stylesheets/components/_alert.scss
new file mode 100644
index 00000000000..48dd85515eb
--- /dev/null
+++ b/app/assets/stylesheets/components/_alert.scss
@@ -0,0 +1,58 @@
+$ico-size: 1rem;
+$ico-offset: 1rem;
+
+.alert {
+ background-color: $blue-lighter;
+ border-radius: $space-1;
+ color: #5b616a;
+ font-size: 1rem;
+ line-height: 1.5rem;
+ margin-bottom: $space-4;
+ padding: 12px $space-2;
+ position: relative;
+
+ &::before {
+ background-image: none;
+ background-repeat: no-repeat;
+ content: '';
+ height: $ico-size;
+ left: $ico-offset;
+ position: absolute;
+ top: $ico-offset;
+ width: $ico-size;
+ }
+}
+
+.alert-success {
+ background-color: #ebfcef;
+ padding-left: $space-4;
+
+ &::before { background-image: url(image-path('alert/success.svg')); }
+}
+
+.alert-thumb {
+ background-color: #ebfcef;
+ padding-left: $space-4;
+
+ &::before { background-image: url(image-path('alert/ico-thumb.svg')); }
+}
+
+.alert-error,
+.alert-alert {
+ background-color: #fff0f3;
+ padding-left: $space-4;
+
+ &::before { background-image: url(image-path('alert/error.svg')); }
+}
+
+.alert-warning {
+ background-color: #fffdd7;
+ padding-left: $space-4;
+
+ &::before { background-image: url(image-path('alert/warning.svg')); }
+}
+
+.alert-notice {
+ padding-left: $space-4;
+ &::before { background-image: url(image-path('alert/notice.svg')); }
+}
diff --git a/app/assets/stylesheets/components/_background.scss b/app/assets/stylesheets/components/_background.scss
new file mode 100644
index 00000000000..9c26230e10c
--- /dev/null
+++ b/app/assets/stylesheets/components/_background.scss
@@ -0,0 +1,9 @@
+.bg-gray-lighter { background-color: $gray-lighter; }
+.bg-light-blue { background-color: $blue-light; }
+.bg-lightest-blue { background-color: $blue-lightest; }
+
+@media #{$breakpoint-sm} {
+ .sm-bg-light-blue { background-color: $blue-light; }
+ .sm-bg-none { background-color: transparent; }
+ .sm-bg-navy { background-color: $navy; }
+}
diff --git a/app/assets/stylesheets/components/_border.scss b/app/assets/stylesheets/components/_border.scss
new file mode 100644
index 00000000000..12256d68ed6
--- /dev/null
+++ b/app/assets/stylesheets/components/_border.scss
@@ -0,0 +1,16 @@
+.bw1 { border-width: 1px; }
+.bw2 { border-width: 2px; }
+.bw3 { border-width: 3px; }
+.bw4 { border-width: 4px; }
+
+.border-dashed { border-style: dashed; }
+
+.rounded-md { border-radius: $border-radius-md; }
+.rounded-lg { border-radius: $border-radius-lg; }
+.rounded-xl { border-radius: $border-radius-xl; }
+.rounded-xxl { border-radius: $border-radius-xxl; }
+
+@media #{$breakpoint-sm} {
+ .sm-border-none { border: 0; }
+ .sm-rounded-md { border-radius: $border-radius-md; }
+}
diff --git a/app/assets/stylesheets/components/_btn.scss b/app/assets/stylesheets/components/_btn.scss
new file mode 100644
index 00000000000..f8f473d80fb
--- /dev/null
+++ b/app/assets/stylesheets/components/_btn.scss
@@ -0,0 +1,80 @@
+@media #{$breakpoint-sm} {
+ .btn { font-size: $button-font-size-sm; }
+}
+
+.btn {
+ white-space: normal;
+}
+
+%btn-basic {
+ background: transparent;
+ border: 0;
+ box-shadow: none;
+ color: $blue;
+ font-size: $base-font-size;
+ font-weight: normal;
+ line-height: $line-height;
+ outline: none;
+ padding: 0;
+}
+
+.btn-primary {
+ border-radius: $border-radius-lg;
+}
+
+.btn-secondary {
+ border: 1px solid $blue;
+ border-radius: $border-radius-lg;
+ color: $blue;
+
+ &:hover {
+ background-color: $blue-lightest;
+ }
+}
+
+.btn-wide {
+ box-sizing: border-box;
+ min-width: 220px;
+ text-align: center;
+}
+
+.btn-transparent {
+ @extend %btn-basic;
+ cursor: pointer;
+}
+
+.btn-link {
+ @extend %btn-basic;
+ text-decoration: underline;
+ vertical-align: baseline;
+
+ &:active,
+ &:focus,
+ &:hover, {
+ border: 0;
+ box-shadow: none;
+ text-decoration: underline;
+ }
+}
+
+.btn-border {
+ border-color: $border-color;
+ border-radius: $border-radius-lg;
+ border-style: solid;
+ border-width: $border-width;
+ box-sizing: border-box;
+ display: inline-block;
+ padding: $space-1 $space-2;
+
+ // &.is-focused {
+ // border-color: $field-focus-color;
+ // box-shadow: 0 0 0 2px rgba($field-focus-color, .5);
+ // outline: none;
+ // }
+}
+
+.btn-disabled {
+ background-color: $gray-light;
+ border-color: $gray;
+ color: $gray;
+}
diff --git a/app/assets/stylesheets/components/_card.scss b/app/assets/stylesheets/components/_card.scss
new file mode 100644
index 00000000000..017d0addade
--- /dev/null
+++ b/app/assets/stylesheets/components/_card.scss
@@ -0,0 +1,23 @@
+.card {
+ background-color: $white;
+ max-width: $container-skinny-width;
+
+ &-wide {
+ max-width: 100%;
+ padding-bottom: 0;
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ @media #{$breakpoint-sm} {
+ border-radius: 5px;
+
+ &-wide {
+ margin-top: $space-4;
+ max-width: 100%;
+ padding-bottom: 0;
+ padding-left: 0;
+ padding-right: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/components/_color.scss b/app/assets/stylesheets/components/_color.scss
new file mode 100644
index 00000000000..572de70adf1
--- /dev/null
+++ b/app/assets/stylesheets/components/_color.scss
@@ -0,0 +1,3 @@
+@media #{$breakpoint-sm} {
+ .sm-white { color: $white; }
+}
diff --git a/app/assets/stylesheets/components/_container.scss b/app/assets/stylesheets/components/_container.scss
new file mode 100644
index 00000000000..5bed3a6d2c0
--- /dev/null
+++ b/app/assets/stylesheets/components/_container.scss
@@ -0,0 +1,7 @@
+.cntnr-skinny { max-width: $container-skinny-width; }
+.cntnr-xskinny { max-width: $container-xskinny-width; }
+.cntnr-xxskinny { max-width: $container-xxskinny-width; }
+
+@media #{$breakpoint-sm} {
+ .cntnr-xxskinny { max-width: $container-xskinny-width; }
+}
diff --git a/app/assets/stylesheets/components/_footer.scss b/app/assets/stylesheets/components/_footer.scss
new file mode 100644
index 00000000000..4b9e8042f51
--- /dev/null
+++ b/app/assets/stylesheets/components/_footer.scss
@@ -0,0 +1,31 @@
+// 1. Avoid the IE 10-11 `min-height` bug.
+// 2. Set `flex-shrink` to `0` to prevent some browsers from
+// letting these items shrink to smaller than their content's default
+// minimum size. See http://bit.ly/1Mn35US for details.
+// 3. Use `%` instead of `vh` since `vh` is buggy in older mobile Safari.
+html {
+ height: 100%;
+}
+
+.site {
+ display: flex;
+ flex-direction: column;
+ height: 100%; // 1, 3
+}
+
+.footer {
+ flex: none; // 2
+ position: relative;
+}
+
+.site-wrap {
+ flex: 1 0 auto; // 2
+ width: 100%;
+}
+
+.site-wrap::after {
+ content: '\00a0'; //
+ display: block;
+ height: 0;
+ visibility: hidden;
+}
diff --git a/app/assets/stylesheets/components/_form.scss b/app/assets/stylesheets/components/_form.scss
new file mode 100644
index 00000000000..71df7c2435c
--- /dev/null
+++ b/app/assets/stylesheets/components/_form.scss
@@ -0,0 +1,272 @@
+$radio-checkbox-space: 1.5rem;
+
+@media #{$breakpoint-sm} {
+ input,
+ select,
+ textarea {
+ font-size: $form-field-font-size-sm;
+ }
+}
+
+label {
+ display: inline-block;
+ margin-bottom: $space-tiny;
+}
+
+textarea {
+ resize: vertical;
+}
+
+.field {
+ background-color: #f2f9ff;
+ color: $gray;
+ font-weight: $bold-font-weight;
+
+ &[type=number],
+ &.phone {
+ font-family: $monospace-font-family;
+ }
+
+ &:focus,
+ &.is-focused {
+ border-color: $field-focus-color;
+ box-shadow: 0 0 0 2px rgba($field-focus-color, .5);
+ outline: none;
+ }
+
+ &:invalid,
+ &.is-error {
+ border-color: $border-color;
+ }
+}
+
+.radio-extra {
+ margin-left: $radio-checkbox-space;
+}
+
+
+// error states
+.has-error input {
+ background-image: url(image-path('alert/error.svg'));
+ background-position: center right $form-field-padding-x;
+ background-repeat: no-repeat;
+ background-size: 1rem 1rem;
+ border-color: $red;
+
+ &.date,
+ &.select {
+ background-image: none;
+ }
+
+ &:focus {
+ border-color: $red;
+ box-shadow: 0 0 0 2px rgba($red, .5);
+ }
+}
+
+// hide number field input spin box as per:
+// http://stackoverflow.com/questions/3790935/can-i-hide-the-html5-number-input-s-spin-box
+// and added .mfa class selector as per CodeClimate warning to:
+// 'Avoid qualifying attribute selectors with an element.'
+.mfa {
+ -moz-appearance: textfield;
+}
+
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+
+// wtf-forms.css
+.checkbox,
+.radio {
+ cursor: pointer;
+ padding-left: 24px;
+ position: relative;
+}
+
+.checkbox input,
+.radio input, {
+ opacity: 0;
+ position: absolute;
+ z-index: -1;
+}
+
+// scss-lint:disable VendorPrefix
+.indicator {
+ background-position: center center;
+ background-repeat: no-repeat;
+ background-size: .5rem .5rem;
+ box-sizing: border-box;
+ display: block;
+ font-size: 65%;
+ height: 1rem;
+ left: 0;
+ line-height: 1rem;
+ position: absolute;
+ text-align: center;
+ top: .25rem;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ width: 1rem;
+}
+
+.checkbox input:focus ~ .indicator,
+.radio input:focus ~ .indicator {
+ box-shadow: 0 0 0 2px rgba($blue, .5);
+}
+
+.checkbox input:checked ~ .indicator,
+.radio input:checked ~ .indicator {
+ background-color: $blue;
+ color: $white;
+}
+
+.checkbox input:active ~ .indicator,
+.radio input:active ~ .indicator {
+ background-color: $blue-light;
+ color: $white;
+}
+
+.checkbox .indicator {
+ background-color: $white;
+ border: $border-width solid $blue;
+ border-radius: .25rem;
+}
+
+.checkbox input:checked ~ .indicator {
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgOCA4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4IDgiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTYuNCwxTDUuNywxLjdMMi45LDQuNUwyLjEsMy43TDEuNCwzTDAsNC40bDAuNywwLjdsMS41LDEuNWwwLjcsMC43bDAuNy0wLjdsMy41LTMuNWwwLjctMC43TDYuNCwxTDYuNCwxeiINCgkvPg0KPC9zdmc+DQo=);
+}
+
+.radio .indicator {
+ background-color: #f2f9ff;
+ border: $border-width solid $blue;
+ border-radius: 50%;
+}
+
+.radio input:checked ~ .indicator {
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgOCA4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4IDgiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTQsMUMyLjMsMSwxLDIuMywxLDRzMS4zLDMsMywzczMtMS4zLDMtM1M1LjcsMSw0LDF6Ii8+DQo8L3N2Zz4NCg==);
+}
+
+.radio input:disabled ~ .indicator {
+ background-color: $gray-light;
+ border-color: $gray;
+}
+
+.select-alt {
+ color: $white;
+ display: inline-block;
+ position: relative;
+ width: 100%;
+
+ select {
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ appearance: none;
+ background-color: $navy;
+ border-right: 1px solid $blue;
+ color: $white;
+ cursor: pointer;
+ display: inline-block;
+ font-weight: normal;
+ line-height: 1.5;
+ padding-right: 2.25rem;
+ width: 100%;
+ }
+
+ // Undo the Firefox inner focus ring
+ select:focus:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 $white;
+ }
+
+ select:active {
+ background-color: $navy;
+ color: $white;
+ }
+
+ // Hide the arrow in IE10 and up
+ select::-ms-expand {
+ display: none;
+ }
+
+ // Separator
+ &::before {
+ border-right: 1px solid $blue;
+ content: '';
+ height: -moz-calc(3rem - 2px);
+ height: -webkit-calc(3rem - 2px);
+ height: calc(3rem - 2px);
+ position: absolute;
+ right: 3rem;
+ top: 1px;
+ width: 0;
+ }
+
+ // Dropdown arrow
+ &::after {
+ border-bottom: .35rem solid transparent;
+ border-left: .35rem solid transparent;
+ border-right: .35rem solid transparent;
+ border-top: .35rem solid;
+ content: '';
+ display: inline-block;
+ height: 0;
+ margin-top: -.15rem;
+ pointer-events: none;
+ position: absolute;
+ right: 1.25rem;
+ top: 50%;
+ width: 0;
+ }
+}
+
+// Media query to target Firefox only
+@-moz-document url-prefix() {
+ // Firefox hack to hide the arrow
+ .select-alt select {
+ padding-right: 1rem;
+ text-indent: .01px;
+ text-overflow: '';
+ }
+
+ //