├── README.md
├── css
├── reset.css
└── style.css
├── index.html
├── js
├── jquery-2.1.1.js
├── main.js
└── modernizr.js
├── partials
├── _layout.scss
├── _mixins.scss
└── _variables.scss
└── scss
└── style.scss
/README.md:
--------------------------------------------------------------------------------
1 | Responsive Newsletter Form
2 | =========
3 |
4 | A minimal and responsive newsletter form with the addition of some subtle CSS3 animations to enrich the user experience.
5 |
6 | [Article on CodyHouse](http://codyhouse.co/gem/responsive-newsletter-form/)
7 |
8 | [Demo](http://codyhouse.co/demo/responsive-newsletter-form/index.html)
9 |
10 | [Terms](http://codyhouse.co/terms/)
11 |
--------------------------------------------------------------------------------
/css/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | footer, header, hgroup, menu, nav, section, main {
29 | display: block;
30 | }
31 | body {
32 | line-height: 1;
33 | }
34 | ol, ul {
35 | list-style: none;
36 | }
37 | blockquote, q {
38 | quotes: none;
39 | }
40 | blockquote:before, blockquote:after,
41 | q:before, q:after {
42 | content: '';
43 | content: none;
44 | }
45 | table {
46 | border-collapse: collapse;
47 | border-spacing: 0;
48 | }
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | /* --------------------------------
2 |
3 | Primary style
4 |
5 | -------------------------------- */
6 | *, *:after, *:before {
7 | -webkit-box-sizing: border-box;
8 | -moz-box-sizing: border-box;
9 | box-sizing: border-box;
10 | }
11 |
12 | body {
13 | font-size: 100%;
14 | font-family: "Open Sans", sans-serif;
15 | color: #f3cb6d;
16 | background-color: white;
17 | }
18 |
19 | a {
20 | color: #444d65;
21 | text-decoration: none;
22 | }
23 |
24 | input, textarea {
25 | font-family: "Open Sans", sans-serif;
26 | font-size: 16px;
27 | font-size: 1rem;
28 | border: none;
29 | margin: 0;
30 | border-radius: 0;
31 | }
32 |
33 | input[type=email]::-ms-clear {
34 | /* remove the big X on ie */
35 | display: none;
36 | }
37 |
38 | input[type=submit] {
39 | -webkit-appearance: none;
40 | -moz-appearance: none;
41 | -ms-appearance: none;
42 | -o-appearance: none;
43 | appearance: none;
44 | }
45 |
46 | /* --------------------------------
47 |
48 | Modules - reusable parts of our design
49 |
50 | -------------------------------- */
51 | .cd-container {
52 | /* this class is used to give a max-width to the element it is applied to, and center it horizontally when it reaches that max-width */
53 | width: 90%;
54 | max-width: 768px;
55 | margin: 0 auto;
56 | }
57 | .cd-container:after {
58 | content: "";
59 | display: table;
60 | clear: both;
61 | }
62 |
63 | /* --------------------------------
64 |
65 | Main components
66 |
67 | -------------------------------- */
68 | header {
69 | position: relative;
70 | height: 200px;
71 | line-height: 200px;
72 | text-align: center;
73 | }
74 | header h1 {
75 | color: #444d65;
76 | font-size: 26px;
77 | font-size: 1.625rem;
78 | font-weight: 300;
79 | }
80 | @media only screen and (min-width: 768px) {
81 | header {
82 | height: 250px;
83 | line-height: 250px;
84 | }
85 | header h1 {
86 | font-size: 30px;
87 | font-size: 1.875rem;
88 | }
89 | }
90 |
91 | /* filter */
92 | .cd-filter {
93 | text-align: center;
94 | }
95 | .cd-filter li {
96 | display: inline-block;
97 | margin: 0 .6em;
98 | }
99 | .cd-filter input[type=radio] {
100 | display: none;
101 | }
102 | .cd-filter label {
103 | display: block;
104 | cursor: pointer;
105 | padding: .6em;
106 | border-radius: 0.25em;
107 | color: rgba(68, 77, 101, 0.4);
108 | }
109 | .cd-filter input:checked + label {
110 | box-shadow: 0 0 0 1px rgba(68, 77, 101, 0.4);
111 | color: #444d65;
112 | }
113 |
114 | /* form */
115 | .cd-form-wrapper {
116 | position: relative;
117 | margin: 4em auto;
118 | }
119 | @media only screen and (min-width: 768px) {
120 | .cd-form-wrapper {
121 | margin: 8em auto;
122 | }
123 | }
124 |
125 | .cd-form {
126 | position: relative;
127 | width: 100%;
128 | height: 100px;
129 | background: #f3cb6d;
130 | }
131 | @media only screen and (min-width: 768px) {
132 | .cd-form {
133 | height: 180px;
134 | }
135 | }
136 | .cd-form .cd-loading {
137 | /* loading bar */
138 | position: absolute;
139 | bottom: 0;
140 | left: 0;
141 | height: 3%;
142 | width: 100%;
143 | background-color: #7f8ba9;
144 | -webkit-transform-origin: 0 50%;
145 | -moz-transform-origin: 0 50%;
146 | -ms-transform-origin: 0 50%;
147 | -o-transform-origin: 0 50%;
148 | transform-origin: 0 50%;
149 | -webkit-transform: scaleX(0);
150 | -moz-transform: scaleX(0);
151 | -ms-transform: scaleX(0);
152 | -o-transform: scaleX(0);
153 | transform: scaleX(0);
154 | visibility: hidden;
155 | -webkit-transition: -webkit-transform 3s;
156 | -moz-transition: -moz-transform 3s;
157 | transition: transform 3s;
158 | z-index: 3;
159 | }
160 | .no-csstransitions .cd-form .cd-loading {
161 | /* we use modernizr to detect old browser and hide the loading effect */
162 | display: none;
163 | }
164 | .cd-form.is-submitted .cd-loading {
165 | visibility: visible;
166 | -webkit-transform: scaleX(1);
167 | -moz-transform: scaleX(1);
168 | -ms-transform: scaleX(1);
169 | -o-transform: scaleX(1);
170 | transform: scaleX(1);
171 | }
172 |
173 | .cd-label, .cd-email, .cd-submit {
174 | position: absolute;
175 | }
176 |
177 | .cd-label {
178 | color: #5a4107;
179 | left: 18px;
180 | top: 20%;
181 | text-transform: uppercase;
182 | font-size: 11px;
183 | font-size: 0.6875rem;
184 | font-weight: 700;
185 | opacity: 1;
186 | -webkit-transform: translate3d(0, 0, 0);
187 | -moz-transform: translate3d(0, 0, 0);
188 | -ms-transform: translate3d(0, 0, 0);
189 | -o-transform: translate3d(0, 0, 0);
190 | transform: translate3d(0, 0, 0);
191 | -webkit-transition: -webkit-transform 0.3s, opacity 0.3s;
192 | -moz-transition: -moz-transform 0.3s, opacity 0.3s;
193 | transition: transform 0.3s, opacity 0.3s;
194 | z-index: 2;
195 | }
196 | .is-active .cd-label {
197 | opacity: 0;
198 | -webkit-transform: translate3d(0, -200%, 0);
199 | -moz-transform: translate3d(0, -200%, 0);
200 | -ms-transform: translate3d(0, -200%, 0);
201 | -o-transform: translate3d(0, -200%, 0);
202 | transform: translate3d(0, -200%, 0);
203 | }
204 | @media only screen and (min-width: 768px) {
205 | .cd-label {
206 | left: 40px;
207 | }
208 | }
209 |
210 | .cd-email, .cd-submit {
211 | width: 100%;
212 | bottom: 0;
213 | }
214 | .cd-email:focus, .cd-submit:focus {
215 | outline: none;
216 | }
217 |
218 | .cd-email {
219 | top: 0;
220 | left: 0;
221 | height: 100%;
222 | background: transparent;
223 | padding-left: 18px;
224 | font-weight: 300;
225 | color: #444d65;
226 | -webkit-transition: height 0.3s, background-color 0.3s;
227 | -moz-transition: height 0.3s, background-color 0.3s;
228 | transition: height 0.3s, background-color 0.3s;
229 | z-index: 1;
230 | }
231 | .cd-email::-webkit-input-placeholder {
232 | color: #a0740d;
233 | }
234 | .cd-email::-moz-placeholder {
235 | color: #a0740d;
236 | }
237 | .cd-email:-moz-placeholder {
238 | color: #a0740d;
239 | }
240 | .cd-email:-ms-input-placeholder {
241 | color: #a0740d;
242 | }
243 | .is-active .cd-email {
244 | height: 50%;
245 | }
246 | .cd-email:focus {
247 | background-color: #f5d385;
248 | }
249 | @media only screen and (min-width: 768px) {
250 | .cd-email {
251 | padding-left: 40px;
252 | font-size: 24px;
253 | font-size: 1.5rem;
254 | }
255 | }
256 |
257 | .cd-submit {
258 | top: 50%;
259 | cursor: pointer;
260 | background-color: #444d65;
261 | color: white;
262 | text-transform: uppercase;
263 | font-weight: 700;
264 | /* hidden by default */
265 | display: none;
266 | -webkit-font-smoothing: antialiased;
267 | -moz-osx-font-smoothing: grayscale;
268 | -webkit-transition: background-color 0.2s;
269 | -moz-transition: background-color 0.2s;
270 | transition: background-color 0.2s;
271 | z-index: 2;
272 | line-height: 50px;
273 | }
274 | .is-active .cd-submit {
275 | display: block;
276 | -webkit-animation: cd-bounce-in ease-out 0.4s;
277 | -moz-animation: cd-bounce-in ease-out 0.4s;
278 | animation: cd-bounce-in ease-out 0.4s;
279 | }
280 | .no-touch .cd-submit:hover, .cd-submit:focus {
281 | background-color: #4e5974;
282 | }
283 | @media only screen and (min-width: 768px) {
284 | .cd-submit {
285 | line-height: 90px;
286 | }
287 | }
288 |
289 | @-webkit-keyframes cd-bounce-in {
290 | 0% {
291 | top: 100%;
292 | }
293 |
294 | 60% {
295 | top: 45%;
296 | }
297 |
298 | 100% {
299 | top: 50%;
300 | }
301 | }
302 | @-moz-keyframes cd-bounce-in {
303 | 0% {
304 | top: 100%;
305 | }
306 |
307 | 60% {
308 | top: 45%;
309 | }
310 |
311 | 100% {
312 | top: 50%;
313 | }
314 | }
315 | @keyframes cd-bounce-in {
316 | 0% {
317 | top: 100%;
318 | }
319 |
320 | 60% {
321 | top: 45%;
322 | }
323 |
324 | 100% {
325 | top: 50%;
326 | }
327 | }
328 | .cd-response {
329 | position: absolute;
330 | bottom: 110%;
331 | left: 0;
332 | padding: 1.4em;
333 | color: white;
334 | font-size: 14px;
335 | font-size: 0.875rem;
336 | -webkit-font-smoothing: antialiased;
337 | -moz-osx-font-smoothing: grayscale;
338 | z-index: 3;
339 | /* hidden by default */
340 | visibility: hidden;
341 | opacity: 0;
342 | -webkit-transform: translateY(-20px);
343 | -moz-transform: translateY(-20px);
344 | -ms-transform: translateY(-20px);
345 | -o-transform: translateY(-20px);
346 | transform: translateY(-20px);
347 | -webkit-transition: -webkit-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s .3s;
348 | -moz-transition: -moz-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s .3s;
349 | transition: transform 0.3s 0s, opacity 0.3s 0s, visibility 0s .3s;
350 | }
351 | .cd-response::after {
352 | content: '';
353 | position: absolute;
354 | top: 100%;
355 | left: 20px;
356 | /* create triangle in css */
357 | display: inline-block;
358 | width: 0;
359 | height: 0;
360 | border: 10px solid transparent;
361 | }
362 | .cd-response.cd-response-error {
363 | background-color: #e36767;
364 | }
365 | .cd-response.cd-response-error::after {
366 | border-top-color: #e36767;
367 | }
368 | .cd-response.cd-response-notification {
369 | background-color: #7f8ba9;
370 | }
371 | .cd-response.cd-response-notification::after {
372 | border-top-color: #7f8ba9;
373 | }
374 | .cd-response.is-visible {
375 | visibility: visible;
376 | opacity: 1;
377 | -webkit-transform: translateY(0);
378 | -moz-transform: translateY(0);
379 | -ms-transform: translateY(0);
380 | -o-transform: translateY(0);
381 | transform: translateY(0);
382 | -webkit-transition: -webkit-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s 0s;
383 | -moz-transition: -moz-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s 0s;
384 | transition: transform 0.3s 0s, opacity 0.3s 0s, visibility 0s 0s;
385 | }
386 |
387 | .cd-response-success {
388 | position: absolute;
389 | bottom: 0;
390 | left: 0;
391 | height: 50%;
392 | width: 100%;
393 | background-color: #92c195;
394 | -webkit-transform-origin: 50% 100%;
395 | -moz-transform-origin: 50% 100%;
396 | -ms-transform-origin: 50% 100%;
397 | -o-transform-origin: 50% 100%;
398 | transform-origin: 50% 100%;
399 | -webkit-transform: scaleY(0);
400 | -moz-transform: scaleY(0);
401 | -ms-transform: scaleY(0);
402 | -o-transform: scaleY(0);
403 | transform: scaleY(0);
404 | visibility: hidden;
405 | -webkit-transition: -webkit-transform .3s 0s, visibility 0s .3s;
406 | -moz-transition: -moz-transform .3s 0s, visibility 0s .3s;
407 | transition: transform .3s 0s, visibility 0s .3s;
408 | z-index: 3;
409 | }
410 | .cd-response-success p {
411 | position: absolute;
412 | text-align: center;
413 | width: 100%;
414 | left: 50%;
415 | top: 50%;
416 | bottom: auto;
417 | right: auto;
418 | -webkit-transform: translateX(-50%) translateY(-50%);
419 | -moz-transform: translateX(-50%) translateY(-50%);
420 | -ms-transform: translateX(-50%) translateY(-50%);
421 | -o-transform: translateX(-50%) translateY(-50%);
422 | transform: translateX(-50%) translateY(-50%);
423 | color: white;
424 | opacity: 0;
425 | -webkit-transition: opacity 0.3s 0s;
426 | -moz-transition: opacity 0.3s 0s;
427 | transition: opacity 0.3s 0s;
428 | }
429 | .cd-response-success.slide-in {
430 | visibility: visible;
431 | -webkit-transform: scaleY(1);
432 | -moz-transform: scaleY(1);
433 | -ms-transform: scaleY(1);
434 | -o-transform: scaleY(1);
435 | transform: scaleY(1);
436 | -webkit-transition: -webkit-transform .3s 0s, visibility 0s 0s;
437 | -moz-transition: -moz-transform .3s 0s, visibility 0s 0s;
438 | transition: transform .3s 0s, visibility 0s 0s;
439 | }
440 | .cd-response-success.slide-in p {
441 | opacity: 1;
442 | -webkit-transition: opacity 0.3s 0.3s;
443 | -moz-transition: opacity 0.3s 0.3s;
444 | transition: opacity 0.3s 0.3s;
445 | }
446 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Responsive Newsletter Form
14 |
15 |
16 |
17 | Newsletter Form
18 |
19 |
20 |
36 |
37 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | jQuery(document).ready(function($){
2 | var messages = $('div[data-type="message"]');
3 | //check if user updates the email field
4 | $('.cd-form .cd-email').keyup(function(event){
5 | //check if user has pressed the enter button (event.which == 13)
6 | if(event.which!= 13) {
7 | //if not..
8 | //hide messages and loading bar
9 | messages.removeClass('slide-in is-visible');
10 | $('.cd-form').removeClass('is-submitted').find('.cd-loading').off('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend');
11 | }
12 |
13 | var emailInput = $(this),
14 | insertedEmail = emailInput.val(),
15 | atPosition = insertedEmail.indexOf("@");
16 | dotPosition = insertedEmail.lastIndexOf(".");
17 | //check if user has inserted a "@" and a dot
18 | if (atPosition< 1 || dotPosition element. This information allows you to progressively enhance
14 | * your pages with a granular level of control over the experience.
15 | *
16 | * Modernizr has an optional (not included) conditional resource loader
17 | * called Modernizr.load(), based on Yepnope.js (yepnopejs.com).
18 | * To get a build that includes Modernizr.load(), as well as choosing
19 | * which tests to include, go to www.modernizr.com/download/
20 | *
21 | * Authors Faruk Ates, Paul Irish, Alex Sexton
22 | * Contributors Ryan Seddon, Ben Alman
23 | */
24 |
25 | window.Modernizr = (function( window, document, undefined ) {
26 |
27 | var version = '2.8.3',
28 |
29 | Modernizr = {},
30 |
31 | /*>>cssclasses*/
32 | // option for enabling the HTML classes to be added
33 | enableClasses = true,
34 | /*>>cssclasses*/
35 |
36 | docElement = document.documentElement,
37 |
38 | /**
39 | * Create our "modernizr" element that we do most feature tests on.
40 | */
41 | mod = 'modernizr',
42 | modElem = document.createElement(mod),
43 | mStyle = modElem.style,
44 |
45 | /**
46 | * Create the input element for various Web Forms feature tests.
47 | */
48 | inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ ,
49 |
50 | /*>>smile*/
51 | smile = ':)',
52 | /*>>smile*/
53 |
54 | toString = {}.toString,
55 |
56 | // TODO :: make the prefixes more granular
57 | /*>>prefixes*/
58 | // List of property values to set for css tests. See ticket #21
59 | prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
60 | /*>>prefixes*/
61 |
62 | /*>>domprefixes*/
63 | // Following spec is to expose vendor-specific style properties as:
64 | // elem.style.WebkitBorderRadius
65 | // and the following would be incorrect:
66 | // elem.style.webkitBorderRadius
67 |
68 | // Webkit ghosts their properties in lowercase but Opera & Moz do not.
69 | // Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
70 | // erik.eae.net/archives/2008/03/10/21.48.10/
71 |
72 | // More here: github.com/Modernizr/Modernizr/issues/issue/21
73 | omPrefixes = 'Webkit Moz O ms',
74 |
75 | cssomPrefixes = omPrefixes.split(' '),
76 |
77 | domPrefixes = omPrefixes.toLowerCase().split(' '),
78 | /*>>domprefixes*/
79 |
80 | /*>>ns*/
81 | ns = {'svg': 'http://www.w3.org/2000/svg'},
82 | /*>>ns*/
83 |
84 | tests = {},
85 | inputs = {},
86 | attrs = {},
87 |
88 | classes = [],
89 |
90 | slice = classes.slice,
91 |
92 | featureName, // used in testing loop
93 |
94 |
95 | /*>>teststyles*/
96 | // Inject element with style element and some CSS rules
97 | injectElementWithStyles = function( rule, callback, nodes, testnames ) {
98 |
99 | var style, ret, node, docOverflow,
100 | div = document.createElement('div'),
101 | // After page load injecting a fake body doesn't work so check if body exists
102 | body = document.body,
103 | // IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it.
104 | fakeBody = body || document.createElement('body');
105 |
106 | if ( parseInt(nodes, 10) ) {
107 | // In order not to give false positives we create a node for each test
108 | // This also allows the method to scale for unspecified uses
109 | while ( nodes-- ) {
110 | node = document.createElement('div');
111 | node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
112 | div.appendChild(node);
113 | }
114 | }
115 |
116 | // '].join('');
122 | div.id = mod;
123 | // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.
124 | // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
125 | (body ? div : fakeBody).innerHTML += style;
126 | fakeBody.appendChild(div);
127 | if ( !body ) {
128 | //avoid crashing IE8, if background image is used
129 | fakeBody.style.background = '';
130 | //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
131 | fakeBody.style.overflow = 'hidden';
132 | docOverflow = docElement.style.overflow;
133 | docElement.style.overflow = 'hidden';
134 | docElement.appendChild(fakeBody);
135 | }
136 |
137 | ret = callback(div, rule);
138 | // If this is done after page load we don't want to remove the body so check if body exists
139 | if ( !body ) {
140 | fakeBody.parentNode.removeChild(fakeBody);
141 | docElement.style.overflow = docOverflow;
142 | } else {
143 | div.parentNode.removeChild(div);
144 | }
145 |
146 | return !!ret;
147 |
148 | },
149 | /*>>teststyles*/
150 |
151 | /*>>mq*/
152 | // adapted from matchMedia polyfill
153 | // by Scott Jehl and Paul Irish
154 | // gist.github.com/786768
155 | testMediaQuery = function( mq ) {
156 |
157 | var matchMedia = window.matchMedia || window.msMatchMedia;
158 | if ( matchMedia ) {
159 | return matchMedia(mq) && matchMedia(mq).matches || false;
160 | }
161 |
162 | var bool;
163 |
164 | injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
165 | bool = (window.getComputedStyle ?
166 | getComputedStyle(node, null) :
167 | node.currentStyle)['position'] == 'absolute';
168 | });
169 |
170 | return bool;
171 |
172 | },
173 | /*>>mq*/
174 |
175 |
176 | /*>>hasevent*/
177 | //
178 | // isEventSupported determines if a given element supports the given event
179 | // kangax.github.com/iseventsupported/
180 | //
181 | // The following results are known incorrects:
182 | // Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative
183 | // Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333
184 | // ...
185 | isEventSupported = (function() {
186 |
187 | var TAGNAMES = {
188 | 'select': 'input', 'change': 'input',
189 | 'submit': 'form', 'reset': 'form',
190 | 'error': 'img', 'load': 'img', 'abort': 'img'
191 | };
192 |
193 | function isEventSupported( eventName, element ) {
194 |
195 | element = element || document.createElement(TAGNAMES[eventName] || 'div');
196 | eventName = 'on' + eventName;
197 |
198 | // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
199 | var isSupported = eventName in element;
200 |
201 | if ( !isSupported ) {
202 | // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
203 | if ( !element.setAttribute ) {
204 | element = document.createElement('div');
205 | }
206 | if ( element.setAttribute && element.removeAttribute ) {
207 | element.setAttribute(eventName, '');
208 | isSupported = is(element[eventName], 'function');
209 |
210 | // If property was created, "remove it" (by setting value to `undefined`)
211 | if ( !is(element[eventName], 'undefined') ) {
212 | element[eventName] = undefined;
213 | }
214 | element.removeAttribute(eventName);
215 | }
216 | }
217 |
218 | element = null;
219 | return isSupported;
220 | }
221 | return isEventSupported;
222 | })(),
223 | /*>>hasevent*/
224 |
225 | // TODO :: Add flag for hasownprop ? didn't last time
226 |
227 | // hasOwnProperty shim by kangax needed for Safari 2.0 support
228 | _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
229 |
230 | if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
231 | hasOwnProp = function (object, property) {
232 | return _hasOwnProperty.call(object, property);
233 | };
234 | }
235 | else {
236 | hasOwnProp = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
237 | return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
238 | };
239 | }
240 |
241 | // Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
242 | // es5.github.com/#x15.3.4.5
243 |
244 | if (!Function.prototype.bind) {
245 | Function.prototype.bind = function bind(that) {
246 |
247 | var target = this;
248 |
249 | if (typeof target != "function") {
250 | throw new TypeError();
251 | }
252 |
253 | var args = slice.call(arguments, 1),
254 | bound = function () {
255 |
256 | if (this instanceof bound) {
257 |
258 | var F = function(){};
259 | F.prototype = target.prototype;
260 | var self = new F();
261 |
262 | var result = target.apply(
263 | self,
264 | args.concat(slice.call(arguments))
265 | );
266 | if (Object(result) === result) {
267 | return result;
268 | }
269 | return self;
270 |
271 | } else {
272 |
273 | return target.apply(
274 | that,
275 | args.concat(slice.call(arguments))
276 | );
277 |
278 | }
279 |
280 | };
281 |
282 | return bound;
283 | };
284 | }
285 |
286 | /**
287 | * setCss applies given styles to the Modernizr DOM node.
288 | */
289 | function setCss( str ) {
290 | mStyle.cssText = str;
291 | }
292 |
293 | /**
294 | * setCssAll extrapolates all vendor-specific css strings.
295 | */
296 | function setCssAll( str1, str2 ) {
297 | return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
298 | }
299 |
300 | /**
301 | * is returns a boolean for if typeof obj is exactly type.
302 | */
303 | function is( obj, type ) {
304 | return typeof obj === type;
305 | }
306 |
307 | /**
308 | * contains returns a boolean for if substr is found within str.
309 | */
310 | function contains( str, substr ) {
311 | return !!~('' + str).indexOf(substr);
312 | }
313 |
314 | /*>>testprop*/
315 |
316 | // testProps is a generic CSS / DOM property test.
317 |
318 | // In testing support for a given CSS property, it's legit to test:
319 | // `elem.style[styleName] !== undefined`
320 | // If the property is supported it will return an empty string,
321 | // if unsupported it will return undefined.
322 |
323 | // We'll take advantage of this quick test and skip setting a style
324 | // on our modernizr element, but instead just testing undefined vs
325 | // empty string.
326 |
327 | // Because the testing of the CSS property names (with "-", as
328 | // opposed to the camelCase DOM properties) is non-portable and
329 | // non-standard but works in WebKit and IE (but not Gecko or Opera),
330 | // we explicitly reject properties with dashes so that authors
331 | // developing in WebKit or IE first don't end up with
332 | // browser-specific content by accident.
333 |
334 | function testProps( props, prefixed ) {
335 | for ( var i in props ) {
336 | var prop = props[i];
337 | if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
338 | return prefixed == 'pfx' ? prop : true;
339 | }
340 | }
341 | return false;
342 | }
343 | /*>>testprop*/
344 |
345 | // TODO :: add testDOMProps
346 | /**
347 | * testDOMProps is a generic DOM property test; if a browser supports
348 | * a certain property, it won't return undefined for it.
349 | */
350 | function testDOMProps( props, obj, elem ) {
351 | for ( var i in props ) {
352 | var item = obj[props[i]];
353 | if ( item !== undefined) {
354 |
355 | // return the property name as a string
356 | if (elem === false) return props[i];
357 |
358 | // let's bind a function
359 | if (is(item, 'function')){
360 | // default to autobind unless override
361 | return item.bind(elem || obj);
362 | }
363 |
364 | // return the unbound function or obj or value
365 | return item;
366 | }
367 | }
368 | return false;
369 | }
370 |
371 | /*>>testallprops*/
372 | /**
373 | * testPropsAll tests a list of DOM properties we want to check against.
374 | * We specify literally ALL possible (known and/or likely) properties on
375 | * the element including the non-vendor prefixed one, for forward-
376 | * compatibility.
377 | */
378 | function testPropsAll( prop, prefixed, elem ) {
379 |
380 | var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
381 | props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
382 |
383 | // did they call .prefixed('boxSizing') or are we just testing a prop?
384 | if(is(prefixed, "string") || is(prefixed, "undefined")) {
385 | return testProps(props, prefixed);
386 |
387 | // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
388 | } else {
389 | props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
390 | return testDOMProps(props, prefixed, elem);
391 | }
392 | }
393 | /*>>testallprops*/
394 |
395 |
396 | /**
397 | * Tests
398 | * -----
399 | */
400 |
401 | // The *new* flexbox
402 | // dev.w3.org/csswg/css3-flexbox
403 |
404 | tests['flexbox'] = function() {
405 | return testPropsAll('flexWrap');
406 | };
407 |
408 | // The *old* flexbox
409 | // www.w3.org/TR/2009/WD-css3-flexbox-20090723/
410 |
411 | tests['flexboxlegacy'] = function() {
412 | return testPropsAll('boxDirection');
413 | };
414 |
415 | // On the S60 and BB Storm, getContext exists, but always returns undefined
416 | // so we actually have to call getContext() to verify
417 | // github.com/Modernizr/Modernizr/issues/issue/97/
418 |
419 | tests['canvas'] = function() {
420 | var elem = document.createElement('canvas');
421 | return !!(elem.getContext && elem.getContext('2d'));
422 | };
423 |
424 | tests['canvastext'] = function() {
425 | return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
426 | };
427 |
428 | // webk.it/70117 is tracking a legit WebGL feature detect proposal
429 |
430 | // We do a soft detect which may false positive in order to avoid
431 | // an expensive context creation: bugzil.la/732441
432 |
433 | tests['webgl'] = function() {
434 | return !!window.WebGLRenderingContext;
435 | };
436 |
437 | /*
438 | * The Modernizr.touch test only indicates if the browser supports
439 | * touch events, which does not necessarily reflect a touchscreen
440 | * device, as evidenced by tablets running Windows 7 or, alas,
441 | * the Palm Pre / WebOS (touch) phones.
442 | *
443 | * Additionally, Chrome (desktop) used to lie about its support on this,
444 | * but that has since been rectified: crbug.com/36415
445 | *
446 | * We also test for Firefox 4 Multitouch Support.
447 | *
448 | * For more info, see: modernizr.github.com/Modernizr/touch.html
449 | */
450 |
451 | tests['touch'] = function() {
452 | var bool;
453 |
454 | if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
455 | bool = true;
456 | } else {
457 | injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
458 | bool = node.offsetTop === 9;
459 | });
460 | }
461 |
462 | return bool;
463 | };
464 |
465 |
466 | // geolocation is often considered a trivial feature detect...
467 | // Turns out, it's quite tricky to get right:
468 | //
469 | // Using !!navigator.geolocation does two things we don't want. It:
470 | // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513
471 | // 2. Disables page caching in WebKit: webk.it/43956
472 | //
473 | // Meanwhile, in Firefox < 8, an about:config setting could expose
474 | // a false positive that would throw an exception: bugzil.la/688158
475 |
476 | tests['geolocation'] = function() {
477 | return 'geolocation' in navigator;
478 | };
479 |
480 |
481 | tests['postmessage'] = function() {
482 | return !!window.postMessage;
483 | };
484 |
485 |
486 | // Chrome incognito mode used to throw an exception when using openDatabase
487 | // It doesn't anymore.
488 | tests['websqldatabase'] = function() {
489 | return !!window.openDatabase;
490 | };
491 |
492 | // Vendors had inconsistent prefixing with the experimental Indexed DB:
493 | // - Webkit's implementation is accessible through webkitIndexedDB
494 | // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
495 | // For speed, we don't test the legacy (and beta-only) indexedDB
496 | tests['indexedDB'] = function() {
497 | return !!testPropsAll("indexedDB", window);
498 | };
499 |
500 | // documentMode logic from YUI to filter out IE8 Compat Mode
501 | // which false positives.
502 | tests['hashchange'] = function() {
503 | return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
504 | };
505 |
506 | // Per 1.6:
507 | // This used to be Modernizr.historymanagement but the longer
508 | // name has been deprecated in favor of a shorter and property-matching one.
509 | // The old API is still available in 1.6, but as of 2.0 will throw a warning,
510 | // and in the first release thereafter disappear entirely.
511 | tests['history'] = function() {
512 | return !!(window.history && history.pushState);
513 | };
514 |
515 | tests['draganddrop'] = function() {
516 | var div = document.createElement('div');
517 | return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
518 | };
519 |
520 | // FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10
521 | // will be supported until FF19 (2/12/13), at which time, ESR becomes FF17.
522 | // FF10 still uses prefixes, so check for it until then.
523 | // for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/
524 | tests['websockets'] = function() {
525 | return 'WebSocket' in window || 'MozWebSocket' in window;
526 | };
527 |
528 |
529 | // css-tricks.com/rgba-browser-support/
530 | tests['rgba'] = function() {
531 | // Set an rgba() color and check the returned value
532 |
533 | setCss('background-color:rgba(150,255,150,.5)');
534 |
535 | return contains(mStyle.backgroundColor, 'rgba');
536 | };
537 |
538 | tests['hsla'] = function() {
539 | // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,
540 | // except IE9 who retains it as hsla
541 |
542 | setCss('background-color:hsla(120,40%,100%,.5)');
543 |
544 | return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
545 | };
546 |
547 | tests['multiplebgs'] = function() {
548 | // Setting multiple images AND a color on the background shorthand property
549 | // and then querying the style.background property value for the number of
550 | // occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
551 |
552 | setCss('background:url(https://),url(https://),red url(https://)');
553 |
554 | // If the UA supports multiple backgrounds, there should be three occurrences
555 | // of the string "url(" in the return value for elemStyle.background
556 |
557 | return (/(url\s*\(.*?){3}/).test(mStyle.background);
558 | };
559 |
560 |
561 |
562 | // this will false positive in Opera Mini
563 | // github.com/Modernizr/Modernizr/issues/396
564 |
565 | tests['backgroundsize'] = function() {
566 | return testPropsAll('backgroundSize');
567 | };
568 |
569 | tests['borderimage'] = function() {
570 | return testPropsAll('borderImage');
571 | };
572 |
573 |
574 | // Super comprehensive table about all the unique implementations of
575 | // border-radius: muddledramblings.com/table-of-css3-border-radius-compliance
576 |
577 | tests['borderradius'] = function() {
578 | return testPropsAll('borderRadius');
579 | };
580 |
581 | // WebOS unfortunately false positives on this test.
582 | tests['boxshadow'] = function() {
583 | return testPropsAll('boxShadow');
584 | };
585 |
586 | // FF3.0 will false positive on this test
587 | tests['textshadow'] = function() {
588 | return document.createElement('div').style.textShadow === '';
589 | };
590 |
591 |
592 | tests['opacity'] = function() {
593 | // Browsers that actually have CSS Opacity implemented have done so
594 | // according to spec, which means their return values are within the
595 | // range of [0.0,1.0] - including the leading zero.
596 |
597 | setCssAll('opacity:.55');
598 |
599 | // The non-literal . in this regex is intentional:
600 | // German Chrome returns this value as 0,55
601 | // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
602 | return (/^0.55$/).test(mStyle.opacity);
603 | };
604 |
605 |
606 | // Note, Android < 4 will pass this test, but can only animate
607 | // a single property at a time
608 | // goo.gl/v3V4Gp
609 | tests['cssanimations'] = function() {
610 | return testPropsAll('animationName');
611 | };
612 |
613 |
614 | tests['csscolumns'] = function() {
615 | return testPropsAll('columnCount');
616 | };
617 |
618 |
619 | tests['cssgradients'] = function() {
620 | /**
621 | * For CSS Gradients syntax, please see:
622 | * webkit.org/blog/175/introducing-css-gradients/
623 | * developer.mozilla.org/en/CSS/-moz-linear-gradient
624 | * developer.mozilla.org/en/CSS/-moz-radial-gradient
625 | * dev.w3.org/csswg/css3-images/#gradients-
626 | */
627 |
628 | var str1 = 'background-image:',
629 | str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
630 | str3 = 'linear-gradient(left top,#9f9, white);';
631 |
632 | setCss(
633 | // legacy webkit syntax (FIXME: remove when syntax not in use anymore)
634 | (str1 + '-webkit- '.split(' ').join(str2 + str1) +
635 | // standard syntax // trailing 'background-image:'
636 | prefixes.join(str3 + str1)).slice(0, -str1.length)
637 | );
638 |
639 | return contains(mStyle.backgroundImage, 'gradient');
640 | };
641 |
642 |
643 | tests['cssreflections'] = function() {
644 | return testPropsAll('boxReflect');
645 | };
646 |
647 |
648 | tests['csstransforms'] = function() {
649 | return !!testPropsAll('transform');
650 | };
651 |
652 |
653 | tests['csstransforms3d'] = function() {
654 |
655 | var ret = !!testPropsAll('perspective');
656 |
657 | // Webkit's 3D transforms are passed off to the browser's own graphics renderer.
658 | // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
659 | // some conditions. As a result, Webkit typically recognizes the syntax but
660 | // will sometimes throw a false positive, thus we must do a more thorough check:
661 | if ( ret && 'webkitPerspective' in docElement.style ) {
662 |
663 | // Webkit allows this media query to succeed only if the feature is enabled.
664 | // `@media (transform-3d),(-webkit-transform-3d){ ... }`
665 | injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
666 | ret = node.offsetLeft === 9 && node.offsetHeight === 3;
667 | });
668 | }
669 | return ret;
670 | };
671 |
672 |
673 | tests['csstransitions'] = function() {
674 | return testPropsAll('transition');
675 | };
676 |
677 |
678 | /*>>fontface*/
679 | // @font-face detection routine by Diego Perini
680 | // javascript.nwbox.com/CSSSupport/
681 |
682 | // false positives:
683 | // WebOS github.com/Modernizr/Modernizr/issues/342
684 | // WP7 github.com/Modernizr/Modernizr/issues/538
685 | tests['fontface'] = function() {
686 | var bool;
687 |
688 | injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) {
689 | var style = document.getElementById('smodernizr'),
690 | sheet = style.sheet || style.styleSheet,
691 | cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
692 |
693 | bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
694 | });
695 |
696 | return bool;
697 | };
698 | /*>>fontface*/
699 |
700 | // CSS generated content detection
701 | tests['generatedcontent'] = function() {
702 | var bool;
703 |
704 | injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) {
705 | bool = node.offsetHeight >= 3;
706 | });
707 |
708 | return bool;
709 | };
710 |
711 |
712 |
713 | // These tests evaluate support of the video/audio elements, as well as
714 | // testing what types of content they support.
715 | //
716 | // We're using the Boolean constructor here, so that we can extend the value
717 | // e.g. Modernizr.video // true
718 | // Modernizr.video.ogg // 'probably'
719 | //
720 | // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
721 | // thx to NielsLeenheer and zcorpan
722 |
723 | // Note: in some older browsers, "no" was a return value instead of empty string.
724 | // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
725 | // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
726 |
727 | tests['video'] = function() {
728 | var elem = document.createElement('video'),
729 | bool = false;
730 |
731 | // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
732 | try {
733 | if ( bool = !!elem.canPlayType ) {
734 | bool = new Boolean(bool);
735 | bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,'');
736 |
737 | // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
738 | bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
739 |
740 | bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
741 | }
742 |
743 | } catch(e) { }
744 |
745 | return bool;
746 | };
747 |
748 | tests['audio'] = function() {
749 | var elem = document.createElement('audio'),
750 | bool = false;
751 |
752 | try {
753 | if ( bool = !!elem.canPlayType ) {
754 | bool = new Boolean(bool);
755 | bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
756 | bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,'');
757 |
758 | // Mimetypes accepted:
759 | // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
760 | // bit.ly/iphoneoscodecs
761 | bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,'');
762 | bool.m4a = ( elem.canPlayType('audio/x-m4a;') ||
763 | elem.canPlayType('audio/aac;')) .replace(/^no$/,'');
764 | }
765 | } catch(e) { }
766 |
767 | return bool;
768 | };
769 |
770 |
771 | // In FF4, if disabled, window.localStorage should === null.
772 |
773 | // Normally, we could not test that directly and need to do a
774 | // `('localStorage' in window) && ` test first because otherwise Firefox will
775 | // throw bugzil.la/365772 if cookies are disabled
776 |
777 | // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
778 | // will throw the exception:
779 | // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
780 | // Peculiarly, getItem and removeItem calls do not throw.
781 |
782 | // Because we are forced to try/catch this, we'll go aggressive.
783 |
784 | // Just FWIW: IE8 Compat mode supports these features completely:
785 | // www.quirksmode.org/dom/html5.html
786 | // But IE8 doesn't support either with local files
787 |
788 | tests['localstorage'] = function() {
789 | try {
790 | localStorage.setItem(mod, mod);
791 | localStorage.removeItem(mod);
792 | return true;
793 | } catch(e) {
794 | return false;
795 | }
796 | };
797 |
798 | tests['sessionstorage'] = function() {
799 | try {
800 | sessionStorage.setItem(mod, mod);
801 | sessionStorage.removeItem(mod);
802 | return true;
803 | } catch(e) {
804 | return false;
805 | }
806 | };
807 |
808 |
809 | tests['webworkers'] = function() {
810 | return !!window.Worker;
811 | };
812 |
813 |
814 | tests['applicationcache'] = function() {
815 | return !!window.applicationCache;
816 | };
817 |
818 |
819 | // Thanks to Erik Dahlstrom
820 | tests['svg'] = function() {
821 | return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
822 | };
823 |
824 | // specifically for SVG inline in HTML, not within XHTML
825 | // test page: paulirish.com/demo/inline-svg
826 | tests['inlinesvg'] = function() {
827 | var div = document.createElement('div');
828 | div.innerHTML = '';
829 | return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
830 | };
831 |
832 | // SVG SMIL animation
833 | tests['smil'] = function() {
834 | return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
835 | };
836 |
837 | // This test is only for clip paths in SVG proper, not clip paths on HTML content
838 | // demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg
839 |
840 | // However read the comments to dig into applying SVG clippaths to HTML content here:
841 | // github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491
842 | tests['svgclippaths'] = function() {
843 | return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
844 | };
845 |
846 | /*>>webforms*/
847 | // input features and input types go directly onto the ret object, bypassing the tests loop.
848 | // Hold this guy to execute in a moment.
849 | function webforms() {
850 | /*>>input*/
851 | // Run through HTML5's new input attributes to see if the UA understands any.
852 | // We're using f which is the element created early on
853 | // Mike Taylr has created a comprehensive resource for testing these attributes
854 | // when applied to all input types:
855 | // miketaylr.com/code/input-type-attr.html
856 | // spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
857 |
858 | // Only input placeholder is tested while textarea's placeholder is not.
859 | // Currently Safari 4 and Opera 11 have support only for the input placeholder
860 | // Both tests are available in feature-detects/forms-placeholder.js
861 | Modernizr['input'] = (function( props ) {
862 | for ( var i = 0, len = props.length; i < len; i++ ) {
863 | attrs[ props[i] ] = !!(props[i] in inputElem);
864 | }
865 | if (attrs.list){
866 | // safari false positive's on datalist: webk.it/74252
867 | // see also github.com/Modernizr/Modernizr/issues/146
868 | attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
869 | }
870 | return attrs;
871 | })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
872 | /*>>input*/
873 |
874 | /*>>inputtypes*/
875 | // Run through HTML5's new input types to see if the UA understands any.
876 | // This is put behind the tests runloop because it doesn't return a
877 | // true/false like all the other tests; instead, it returns an object
878 | // containing each input type with its corresponding true/false value
879 |
880 | // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/
881 | Modernizr['inputtypes'] = (function(props) {
882 |
883 | for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
884 |
885 | inputElem.setAttribute('type', inputElemType = props[i]);
886 | bool = inputElem.type !== 'text';
887 |
888 | // We first check to see if the type we give it sticks..
889 | // If the type does, we feed it a textual value, which shouldn't be valid.
890 | // If the value doesn't stick, we know there's input sanitization which infers a custom UI
891 | if ( bool ) {
892 |
893 | inputElem.value = smile;
894 | inputElem.style.cssText = 'position:absolute;visibility:hidden;';
895 |
896 | if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
897 |
898 | docElement.appendChild(inputElem);
899 | defaultView = document.defaultView;
900 |
901 | // Safari 2-4 allows the smiley as a value, despite making a slider
902 | bool = defaultView.getComputedStyle &&
903 | defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
904 | // Mobile android web browser has false positive, so must
905 | // check the height to see if the widget is actually there.
906 | (inputElem.offsetHeight !== 0);
907 |
908 | docElement.removeChild(inputElem);
909 |
910 | } else if ( /^(search|tel)$/.test(inputElemType) ){
911 | // Spec doesn't define any special parsing or detectable UI
912 | // behaviors so we pass these through as true
913 |
914 | // Interestingly, opera fails the earlier test, so it doesn't
915 | // even make it here.
916 |
917 | } else if ( /^(url|email)$/.test(inputElemType) ) {
918 | // Real url and email support comes with prebaked validation.
919 | bool = inputElem.checkValidity && inputElem.checkValidity() === false;
920 |
921 | } else {
922 | // If the upgraded input compontent rejects the :) text, we got a winner
923 | bool = inputElem.value != smile;
924 | }
925 | }
926 |
927 | inputs[ props[i] ] = !!bool;
928 | }
929 | return inputs;
930 | })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
931 | /*>>inputtypes*/
932 | }
933 | /*>>webforms*/
934 |
935 |
936 | // End of test definitions
937 | // -----------------------
938 |
939 |
940 |
941 | // Run through all tests and detect their support in the current UA.
942 | // todo: hypothetically we could be doing an array of tests and use a basic loop here.
943 | for ( var feature in tests ) {
944 | if ( hasOwnProp(tests, feature) ) {
945 | // run the test, throw the return value into the Modernizr,
946 | // then based on that boolean, define an appropriate className
947 | // and push it into an array of classes we'll join later.
948 | featureName = feature.toLowerCase();
949 | Modernizr[featureName] = tests[feature]();
950 |
951 | classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
952 | }
953 | }
954 |
955 | /*>>webforms*/
956 | // input tests need to run.
957 | Modernizr.input || webforms();
958 | /*>>webforms*/
959 |
960 |
961 | /**
962 | * addTest allows the user to define their own feature tests
963 | * the result will be added onto the Modernizr object,
964 | * as well as an appropriate className set on the html element
965 | *
966 | * @param feature - String naming the feature
967 | * @param test - Function returning true if feature is supported, false if not
968 | */
969 | Modernizr.addTest = function ( feature, test ) {
970 | if ( typeof feature == 'object' ) {
971 | for ( var key in feature ) {
972 | if ( hasOwnProp( feature, key ) ) {
973 | Modernizr.addTest( key, feature[ key ] );
974 | }
975 | }
976 | } else {
977 |
978 | feature = feature.toLowerCase();
979 |
980 | if ( Modernizr[feature] !== undefined ) {
981 | // we're going to quit if you're trying to overwrite an existing test
982 | // if we were to allow it, we'd do this:
983 | // var re = new RegExp("\\b(no-)?" + feature + "\\b");
984 | // docElement.className = docElement.className.replace( re, '' );
985 | // but, no rly, stuff 'em.
986 | return Modernizr;
987 | }
988 |
989 | test = typeof test == 'function' ? test() : test;
990 |
991 | if (typeof enableClasses !== "undefined" && enableClasses) {
992 | docElement.className += ' ' + (test ? '' : 'no-') + feature;
993 | }
994 | Modernizr[feature] = test;
995 |
996 | }
997 |
998 | return Modernizr; // allow chaining.
999 | };
1000 |
1001 |
1002 | // Reset modElem.cssText to nothing to reduce memory footprint.
1003 | setCss('');
1004 | modElem = inputElem = null;
1005 |
1006 | /*>>shiv*/
1007 | /**
1008 | * @preserve HTML5 Shiv prev3.7.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
1009 | */
1010 | ;(function(window, document) {
1011 | /*jshint evil:true */
1012 | /** version */
1013 | var version = '3.7.0';
1014 |
1015 | /** Preset options */
1016 | var options = window.html5 || {};
1017 |
1018 | /** Used to skip problem elements */
1019 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
1020 |
1021 | /** Not all elements can be cloned in IE **/
1022 | var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
1023 |
1024 | /** Detect whether the browser supports default html5 styles */
1025 | var supportsHtml5Styles;
1026 |
1027 | /** Name of the expando, to work with multiple documents or to re-shiv one document */
1028 | var expando = '_html5shiv';
1029 |
1030 | /** The id for the the documents expando */
1031 | var expanID = 0;
1032 |
1033 | /** Cached data for each document */
1034 | var expandoData = {};
1035 |
1036 | /** Detect whether the browser supports unknown elements */
1037 | var supportsUnknownElements;
1038 |
1039 | (function() {
1040 | try {
1041 | var a = document.createElement('a');
1042 | a.innerHTML = '';
1043 | //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
1044 | supportsHtml5Styles = ('hidden' in a);
1045 |
1046 | supportsUnknownElements = a.childNodes.length == 1 || (function() {
1047 | // assign a false positive if unable to shiv
1048 | (document.createElement)('a');
1049 | var frag = document.createDocumentFragment();
1050 | return (
1051 | typeof frag.cloneNode == 'undefined' ||
1052 | typeof frag.createDocumentFragment == 'undefined' ||
1053 | typeof frag.createElement == 'undefined'
1054 | );
1055 | }());
1056 | } catch(e) {
1057 | // assign a false positive if detection fails => unable to shiv
1058 | supportsHtml5Styles = true;
1059 | supportsUnknownElements = true;
1060 | }
1061 |
1062 | }());
1063 |
1064 | /*--------------------------------------------------------------------------*/
1065 |
1066 | /**
1067 | * Creates a style sheet with the given CSS text and adds it to the document.
1068 | * @private
1069 | * @param {Document} ownerDocument The document.
1070 | * @param {String} cssText The CSS text.
1071 | * @returns {StyleSheet} The style element.
1072 | */
1073 | function addStyleSheet(ownerDocument, cssText) {
1074 | var p = ownerDocument.createElement('p'),
1075 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
1076 |
1077 | p.innerHTML = 'x';
1078 | return parent.insertBefore(p.lastChild, parent.firstChild);
1079 | }
1080 |
1081 | /**
1082 | * Returns the value of `html5.elements` as an array.
1083 | * @private
1084 | * @returns {Array} An array of shived element node names.
1085 | */
1086 | function getElements() {
1087 | var elements = html5.elements;
1088 | return typeof elements == 'string' ? elements.split(' ') : elements;
1089 | }
1090 |
1091 | /**
1092 | * Returns the data associated to the given document
1093 | * @private
1094 | * @param {Document} ownerDocument The document.
1095 | * @returns {Object} An object of data.
1096 | */
1097 | function getExpandoData(ownerDocument) {
1098 | var data = expandoData[ownerDocument[expando]];
1099 | if (!data) {
1100 | data = {};
1101 | expanID++;
1102 | ownerDocument[expando] = expanID;
1103 | expandoData[expanID] = data;
1104 | }
1105 | return data;
1106 | }
1107 |
1108 | /**
1109 | * returns a shived element for the given nodeName and document
1110 | * @memberOf html5
1111 | * @param {String} nodeName name of the element
1112 | * @param {Document} ownerDocument The context document.
1113 | * @returns {Object} The shived element.
1114 | */
1115 | function createElement(nodeName, ownerDocument, data){
1116 | if (!ownerDocument) {
1117 | ownerDocument = document;
1118 | }
1119 | if(supportsUnknownElements){
1120 | return ownerDocument.createElement(nodeName);
1121 | }
1122 | if (!data) {
1123 | data = getExpandoData(ownerDocument);
1124 | }
1125 | var node;
1126 |
1127 | if (data.cache[nodeName]) {
1128 | node = data.cache[nodeName].cloneNode();
1129 | } else if (saveClones.test(nodeName)) {
1130 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
1131 | } else {
1132 | node = data.createElem(nodeName);
1133 | }
1134 |
1135 | // Avoid adding some elements to fragments in IE < 9 because
1136 | // * Attributes like `name` or `type` cannot be set/changed once an element
1137 | // is inserted into a document/fragment
1138 | // * Link elements with `src` attributes that are inaccessible, as with
1139 | // a 403 response, will cause the tab/window to crash
1140 | // * Script elements appended to fragments will execute when their `src`
1141 | // or `text` property is set
1142 | return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
1143 | }
1144 |
1145 | /**
1146 | * returns a shived DocumentFragment for the given document
1147 | * @memberOf html5
1148 | * @param {Document} ownerDocument The context document.
1149 | * @returns {Object} The shived DocumentFragment.
1150 | */
1151 | function createDocumentFragment(ownerDocument, data){
1152 | if (!ownerDocument) {
1153 | ownerDocument = document;
1154 | }
1155 | if(supportsUnknownElements){
1156 | return ownerDocument.createDocumentFragment();
1157 | }
1158 | data = data || getExpandoData(ownerDocument);
1159 | var clone = data.frag.cloneNode(),
1160 | i = 0,
1161 | elems = getElements(),
1162 | l = elems.length;
1163 | for(;i>shiv*/
1309 |
1310 | // Assign private properties to the return object with prefix
1311 | Modernizr._version = version;
1312 |
1313 | // expose these for the plugin API. Look in the source for how to join() them against your input
1314 | /*>>prefixes*/
1315 | Modernizr._prefixes = prefixes;
1316 | /*>>prefixes*/
1317 | /*>>domprefixes*/
1318 | Modernizr._domPrefixes = domPrefixes;
1319 | Modernizr._cssomPrefixes = cssomPrefixes;
1320 | /*>>domprefixes*/
1321 |
1322 | /*>>mq*/
1323 | // Modernizr.mq tests a given media query, live against the current state of the window
1324 | // A few important notes:
1325 | // * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false
1326 | // * A max-width or orientation query will be evaluated against the current state, which may change later.
1327 | // * You must specify values. Eg. If you are testing support for the min-width media query use:
1328 | // Modernizr.mq('(min-width:0)')
1329 | // usage:
1330 | // Modernizr.mq('only screen and (max-width:768)')
1331 | Modernizr.mq = testMediaQuery;
1332 | /*>>mq*/
1333 |
1334 | /*>>hasevent*/
1335 | // Modernizr.hasEvent() detects support for a given event, with an optional element to test on
1336 | // Modernizr.hasEvent('gesturestart', elem)
1337 | Modernizr.hasEvent = isEventSupported;
1338 | /*>>hasevent*/
1339 |
1340 | /*>>testprop*/
1341 | // Modernizr.testProp() investigates whether a given style property is recognized
1342 | // Note that the property names must be provided in the camelCase variant.
1343 | // Modernizr.testProp('pointerEvents')
1344 | Modernizr.testProp = function(prop){
1345 | return testProps([prop]);
1346 | };
1347 | /*>>testprop*/
1348 |
1349 | /*>>testallprops*/
1350 | // Modernizr.testAllProps() investigates whether a given style property,
1351 | // or any of its vendor-prefixed variants, is recognized
1352 | // Note that the property names must be provided in the camelCase variant.
1353 | // Modernizr.testAllProps('boxSizing')
1354 | Modernizr.testAllProps = testPropsAll;
1355 | /*>>testallprops*/
1356 |
1357 |
1358 | /*>>teststyles*/
1359 | // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards
1360 | // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })
1361 | Modernizr.testStyles = injectElementWithStyles;
1362 | /*>>teststyles*/
1363 |
1364 |
1365 | /*>>prefixed*/
1366 | // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input
1367 | // Modernizr.prefixed('boxSizing') // 'MozBoxSizing'
1368 |
1369 | // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.
1370 | // Return values will also be the camelCase variant, if you need to translate that to hypenated style use:
1371 | //
1372 | // str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
1373 |
1374 | // If you're trying to ascertain which transition end event to bind to, you might do something like...
1375 | //
1376 | // var transEndEventNames = {
1377 | // 'WebkitTransition' : 'webkitTransitionEnd',
1378 | // 'MozTransition' : 'transitionend',
1379 | // 'OTransition' : 'oTransitionEnd',
1380 | // 'msTransition' : 'MSTransitionEnd',
1381 | // 'transition' : 'transitionend'
1382 | // },
1383 | // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
1384 |
1385 | Modernizr.prefixed = function(prop, obj, elem){
1386 | if(!obj) {
1387 | return testPropsAll(prop, 'pfx');
1388 | } else {
1389 | // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
1390 | return testPropsAll(prop, obj, elem);
1391 | }
1392 | };
1393 | /*>>prefixed*/
1394 |
1395 |
1396 | /*>>cssclasses*/
1397 | // Remove "no-js" class from element, if it exists:
1398 | docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
1399 |
1400 | // Add the new classes to the element.
1401 | (enableClasses ? ' js ' + classes.join(' ') : '');
1402 | /*>>cssclasses*/
1403 |
1404 | return Modernizr;
1405 |
1406 | })(this, this.document);
1407 |
--------------------------------------------------------------------------------
/partials/_layout.scss:
--------------------------------------------------------------------------------
1 | // breakpoints
2 |
3 | $S: 320px;
4 | $M: 768px;
5 | $L: 1170px;
6 |
7 | // media queries
8 |
9 | @mixin MQ($canvas) {
10 | @if $canvas == S {
11 | @media only screen and (min-width: $S) { @content; }
12 | }
13 | @else if $canvas == M {
14 | @media only screen and (min-width: $M) { @content; }
15 | }
16 | @else if $canvas == L {
17 | @media only screen and (min-width: $L) { @content; }
18 | }
19 | }
20 |
21 | // super light grid - it works with the .cd-container class inside style.scss
22 |
23 | @mixin column($percentage, $float-direction:left) {
24 | width: 100% * $percentage;
25 | float: $float-direction;
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/partials/_mixins.scss:
--------------------------------------------------------------------------------
1 | // rem fallback - credits: http://zerosixthree.se/
2 |
3 | @function calculateRem($size) {
4 | $remSize: $size / 16px;
5 | @return $remSize * 1rem;
6 | }
7 |
8 | @mixin font-size($size) {
9 | font-size: $size;
10 | font-size: calculateRem($size);
11 | }
12 |
13 | // center vertically and/or horizontally an absolute positioned element
14 |
15 | @mixin center($xy:xy) {
16 | @if $xy == xy {
17 | left: 50%;
18 | top: 50%;
19 | bottom: auto;
20 | right: auto;
21 | @include transform(translateX(-50%) translateY(-50%));
22 | }
23 | @else if $xy == x {
24 | left: 50%;
25 | right: auto;
26 | @include transform(translateX(-50%));
27 | }
28 | @else if $xy == y {
29 | top: 50%;
30 | bottom: auto;
31 | @include transform(translateY(-50%));
32 | }
33 | }
34 |
35 | // border radius
36 |
37 | @mixin border-radius($radius:.25em) {
38 | border-radius: $radius;
39 | }
40 |
41 | // antialiasing mode font rendering
42 |
43 | @mixin font-smoothing {
44 | -webkit-font-smoothing: antialiased;
45 | -moz-osx-font-smoothing: grayscale;
46 | }
47 |
--------------------------------------------------------------------------------
/partials/_variables.scss:
--------------------------------------------------------------------------------
1 | // colors
2 |
3 | $color-1: #f3cb6d; // yellow
4 | $color-2: #444d65; // blue
5 | $color-3: #fff; // body background color
6 | $color-4: #e36767; // red
7 | $color-5: #92c195; // green
8 |
9 | // fonts
10 |
11 | $primary-font: 'Open Sans', sans-serif;
--------------------------------------------------------------------------------
/scss/style.scss:
--------------------------------------------------------------------------------
1 | @import 'bourbon'; // http://bourbon.io/
2 |
3 | @import '../partials/variables'; // colors, fonts etc...
4 |
5 | @import '../partials/mixins'; // custom mixins
6 |
7 | @import '../partials/layout'; // responsive grid and media queries
8 |
9 | /* --------------------------------
10 |
11 | Primary style
12 |
13 | -------------------------------- */
14 |
15 | *, *:after, *:before {
16 | @include box-sizing(border-box);
17 | }
18 |
19 | body {
20 | font: {
21 | size: 100%;
22 | family: $primary-font; // variables inside partials > _variables.scss
23 | }
24 | color: $color-1;
25 | background-color: $color-3;
26 | }
27 |
28 | a {
29 | color: $color-2;
30 | text-decoration: none;
31 | }
32 |
33 | input, textarea {
34 | font-family: $primary-font;
35 | @include font-size(16px);
36 | border: none;
37 | margin: 0;
38 | border-radius: 0;
39 | }
40 |
41 | input[type=email]::-ms-clear {
42 | /* remove the big X on ie */
43 | display: none;
44 | }
45 |
46 | input[type=submit] {
47 | @include appearance(none);
48 | }
49 |
50 | /* --------------------------------
51 |
52 | Modules - reusable parts of our design
53 |
54 | -------------------------------- */
55 |
56 | .cd-container { /* this class is used to give a max-width to the element it is applied to, and center it horizontally when it reaches that max-width */
57 | width: 90%;
58 | max-width: $M; // breakpoints inside partials > _layout.scss
59 | margin: 0 auto;
60 | @include clearfix;
61 | }
62 |
63 | /* --------------------------------
64 |
65 | Main components
66 |
67 | -------------------------------- */
68 |
69 | header {
70 | position: relative;
71 | height: 200px;
72 | line-height: 200px;
73 | text-align: center;
74 |
75 | h1 {
76 | color: $color-2;
77 | @include font-size(26px);
78 | font-weight: 300;
79 | }
80 |
81 | @include MQ(M) { // to change the breakpoints see _layout.scss
82 | height: 250px;
83 | line-height: 250px;
84 |
85 | h1 {
86 | @include font-size(30px);
87 | }
88 | }
89 | }
90 |
91 | /* filter */
92 |
93 | .cd-filter {
94 | text-align: center;
95 |
96 | li {
97 | display: inline-block;
98 | margin: 0 .6em;
99 | }
100 |
101 | input[type=radio] {
102 | display: none;
103 | }
104 |
105 | label {
106 | display: block;
107 | cursor: pointer;
108 | padding: .6em;
109 | @include border-radius;
110 | color: rgba($color-2, .4);
111 | }
112 |
113 | input:checked + label {
114 | box-shadow: 0 0 0 1px rgba($color-2, .4);
115 | color: $color-2;
116 | }
117 | }
118 |
119 | /* form */
120 |
121 | .cd-form-wrapper {
122 | position: relative;
123 | margin: 4em auto;
124 |
125 | @include MQ(M) {
126 | margin: 8em auto;
127 | }
128 | }
129 |
130 | .cd-form {
131 | position: relative;
132 | width: 100%;
133 | height: 100px;
134 | background: $color-1;
135 |
136 | @include MQ(M) {
137 | height: 180px;
138 | }
139 |
140 | .cd-loading {
141 | /* loading bar */
142 | position: absolute;
143 | bottom: 0;
144 | left: 0;
145 | height: 3%;
146 | width: 100%;
147 | background-color: lighten($color-2, 25%);
148 |
149 | @include transform-origin(0 50%);
150 | @include transform(scaleX(0));
151 | visibility: hidden;
152 | -webkit-transition: -webkit-transform 3s;
153 | -moz-transition: -moz-transform 3s;
154 | transition: transform 3s;
155 | z-index: 3;
156 |
157 | .no-csstransitions & {
158 | /* we use modernizr to detect old browser and hide the loading effect */
159 | display: none;
160 | }
161 | }
162 |
163 | &.is-submitted .cd-loading{
164 | visibility: visible;
165 | @include transform(scaleX(1));
166 | }
167 | }
168 |
169 | .cd-label, .cd-email, .cd-submit {
170 | position: absolute;
171 | }
172 |
173 | .cd-label {
174 | color: darken($color-1, 50%);
175 | left: 18px;
176 | top: 20%;
177 | text-transform: uppercase;
178 | @include font-size(11px);
179 | font-weight: 700;
180 | opacity: 1;
181 | @include transform(translate3d(0,0,0));
182 | -webkit-transition: -webkit-transform 0.3s, opacity 0.3s;
183 | -moz-transition: -moz-transform 0.3s, opacity 0.3s;
184 | transition: transform 0.3s, opacity 0.3s;
185 | z-index: 2;
186 |
187 | .is-active & {
188 | opacity: 0;
189 | @include transform(translate3d(0,-200%,0));
190 | }
191 |
192 | @include MQ(M) {
193 | left: 40px;
194 | }
195 | }
196 |
197 | .cd-email, .cd-submit {
198 | width: 100%;
199 | bottom: 0;
200 |
201 | &:focus {
202 | outline: none;
203 | }
204 | }
205 |
206 | .cd-email {
207 | top: 0;
208 | left: 0;
209 | height: 100%;
210 | background: transparent;
211 | padding-left: 18px;
212 | font-weight: 300;
213 | color: $color-2;
214 | @include transition(height .3s, background-color .3s);
215 | z-index: 1;
216 |
217 | @include placeholder {
218 | color: darken($color-1, 35%);
219 | }
220 |
221 | .is-active & {
222 | height: 50%;
223 | }
224 |
225 | &:focus {
226 | background-color: lighten($color-1, 5%);
227 | }
228 |
229 | @include MQ(M) {
230 | padding-left: 40px;
231 | @include font-size(24px);
232 | }
233 | }
234 |
235 | .cd-submit {
236 | top: 50%;
237 | cursor: pointer;
238 | background-color: $color-2;
239 | color: $color-3;
240 | text-transform: uppercase;
241 | font-weight: 700;
242 | /* hidden by default */
243 | display: none;
244 | @include font-smoothing;
245 | @include transition(background-color .2s);
246 | z-index: 2;
247 | line-height: 50px;
248 |
249 | .is-active & {
250 | display: block;
251 | @include animation(cd-bounce-in ease-out .4s);
252 | }
253 |
254 | .no-touch &:hover, &:focus {
255 | background-color: lighten($color-2, 5%);
256 | }
257 |
258 | @include MQ(M) {
259 | line-height: 90px;
260 | }
261 | }
262 |
263 | @include keyframes(cd-bounce-in) {
264 | 0% {
265 | top: 100%;
266 | }
267 |
268 | 60% {
269 | top: 45%;
270 | }
271 |
272 | 100% {
273 | top: 50%;
274 | }
275 | }
276 |
277 | .cd-response {
278 | position: absolute;
279 | bottom: 110%;
280 | left: 0;
281 | padding: 1.4em;
282 | color: $color-3;
283 | @include font-size(14px);
284 | @include font-smoothing;
285 | z-index: 3;
286 |
287 | /* hidden by default */
288 | visibility: hidden;
289 | opacity: 0;
290 | @include transform(translateY(-20px));
291 |
292 | -webkit-transition: -webkit-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s .3s;
293 | -moz-transition: -moz-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s .3s;
294 | transition: transform 0.3s 0s, opacity 0.3s 0s, visibility 0s .3s;
295 |
296 | &::after {
297 | content: '';
298 | position: absolute;
299 | top: 100%;
300 | left: 20px;
301 |
302 | /* create triangle in css */
303 | display: inline-block;
304 | width: 0;
305 | height: 0;
306 | border: 10px solid transparent;
307 | }
308 |
309 | &.cd-response-error {
310 | background-color: $color-4;
311 |
312 | &::after {
313 | border-top-color: $color-4;
314 | }
315 | }
316 |
317 | &.cd-response-notification {
318 | background-color: lighten($color-2, 25%);
319 |
320 | &::after {
321 | border-top-color: lighten($color-2, 25%);
322 | }
323 | }
324 |
325 | &.is-visible {
326 | visibility: visible;
327 | opacity: 1;
328 | @include transform(translateY(0));
329 |
330 | -webkit-transition: -webkit-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s 0s;
331 | -moz-transition: -moz-transform 0.3s 0s, opacity 0.3s 0s, visibility 0s 0s;
332 | transition: transform 0.3s 0s, opacity 0.3s 0s, visibility 0s 0s;
333 | }
334 | }
335 |
336 | .cd-response-success {
337 | position: absolute;
338 | bottom: 0;
339 | left: 0;
340 | height: 50%;
341 | width: 100%;
342 | background-color: $color-5;
343 | @include transform-origin(50% 100%);
344 | @include transform(scaleY(0));
345 | visibility: hidden;
346 | -webkit-transition: -webkit-transform .3s 0s, visibility 0s .3s;
347 | -moz-transition: -moz-transform .3s 0s, visibility 0s .3s;
348 | transition: transform .3s 0s, visibility 0s .3s;
349 | z-index: 3;
350 |
351 | p {
352 | position: absolute;
353 | text-align: center;
354 | width: 100%;
355 | @include center;
356 | color: $color-3;
357 | opacity: 0;
358 | @include transition(opacity .3s 0s);
359 | }
360 |
361 | &.slide-in {
362 | visibility: visible;
363 | @include transform(scaleY(1));
364 |
365 | -webkit-transition: -webkit-transform .3s 0s, visibility 0s 0s;
366 | -moz-transition: -moz-transform .3s 0s, visibility 0s 0s;
367 | transition: transform .3s 0s, visibility 0s 0s;
368 |
369 | p {
370 | opacity: 1;
371 | @include transition(opacity .3s .3s);
372 | }
373 | }
374 | }
--------------------------------------------------------------------------------