├── .gitignore ├── README.md ├── assets ├── images │ └── favicon.png ├── js │ ├── app.js │ ├── components │ │ └── component.js │ └── modernizr.js └── scss │ ├── app.scss │ ├── base.scss │ ├── components │ ├── alpinejs.scss │ └── links.scss │ └── utilities │ ├── font-face.scss │ ├── rem.scss │ └── transition.scss ├── gulpfile.js ├── package-lock.json ├── package.json ├── tailwind.config.js └── web ├── dist ├── css │ └── app.css ├── images │ └── favicon.png └── js │ └── app.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Gulp / Packages 2 | 3 | node_modules 4 | 5 | # Sass 6 | 7 | *.sassc 8 | .sass-cache/* 9 | .sass-cache 10 | *.css.map 11 | 12 | # Javascript 13 | 14 | *.js.map 15 | 16 | # OS Files 17 | 18 | .DS_Store 19 | *.DS_Store 20 | .DS_Store? 21 | ._* 22 | .Spotlight-V100 23 | .Trashes 24 | ehthumbs.db 25 | Thumbs.db 26 | 27 | # Craft CMS 28 | 29 | cpresources 30 | vendor 31 | .env 32 | *.log 33 | storage/* 34 | config/license.key 35 | 36 | # Asset Source Folders 37 | 38 | web/uploads 39 | public/uploads 40 | public_html/uploads 41 | 42 | # Log files 43 | 44 | npm-debug.log* 45 | yarn-debug.log* 46 | yarn-error.log* 47 | 48 | # Editor directories and files 49 | 50 | .idea 51 | .vscode 52 | *.suo 53 | *.ntvs* 54 | *.njsproj 55 | *.sln 56 | *.sw* 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tailwind CSS 2 | 3 | This is a boilerplate [MadeByShape](https://madebyshape.co.uk) use internally for projects. 4 | 5 | It utilises the [Tailwind CSS] (https://tailwindcss.com) framework, to enable rapid development using utility / helper classes, with a mixture of BEM. 6 | 7 | ## Contents 8 | 9 | - [Installing](#installing) 10 | - [Compiling](#compiling) 11 | - [JS/CSS Dependencies](#js-css-dependencies) 12 | - [Framework](#framework) 13 | - [Framework Plugins](#framework-plugins) 14 | - [Colours](#colours) 15 | - [CSS Blocks](#css-blocks) 16 | - [CSS Helpers](#css-helpers) 17 | - [CSS Utilities](#css-utilities) 18 | - [JS Components](#js-components) 19 | - [JS Naming](#js-naming) 20 | - [Favicons](#favicons) 21 | 22 | ### Installing 23 | 24 | @TODO 25 | 26 | ### Compiling 27 | 28 | Use Gulp to run tasks within the project. See the `gulpfile.js` file for a list of available individual tasks. The build tasks are: 29 | 30 | - `gulp dev` 31 | - `gulp production` 32 | 33 | All Gulp settings are stored in the `package.json` file, e.g. If you need to change the public folder from `web/` then edit `paths > public` in this file. 34 | 35 | All assets should be placed in the root folder `assets`, seperated in to subfolders (E.g. `scss`, `js` etc). Use the `web/dist` folder to store any public files. 36 | 37 | Any files placed inside `assets` will be compiled, minified and optimised and then moved to the `dist` folder. 38 | 39 | NOTE: Never place files directly into the `web/dist` folder, as these can be overwritten sometimes via build commands. 40 | 41 | ### JS/CSS Dependencies 42 | 43 | Install all plugins and library dependencies using `npm`. Use the `cssDependencies` and `jsDependencies` arrays in `package.json` to add dependency files straight from the `node_modules` folder. 44 | 45 | For example, if the JS file required for the project is located in `path/to/the/node_modules/jquery/dist/jquery.js` add this path to the `jsDependencies` array without the `node_modules` folder (`jquery/dist/jquery.js`) and compile. 46 | 47 | ### Framework 48 | 49 | [Tailwind CSS] (https://tailwindcss.com) is used as the core framework - Read the docs to use this framework https://tailwindcss.com/docs 50 | 51 | Edit the `tailwind.config.js` whenever you need to modify / add new CSS utility classes. 52 | 53 | Use the `@apply` in custom CSS components where possible. 54 | 55 | For a grid, use Flex box mixed with width utilities as specified in the Tailwind CSS docs (https://tailwindcss.com/components/grids) 56 | 57 | ### Framework Plugins 58 | 59 | - Aspect Ratio (https://github.com/webdna/tailwindcss-aspect-ratio) 60 | - Transitions (https://github.com/benface/tailwindcss-transitions) 61 | - Transforms (https://github.com/benface/tailwindcss-transforms) 62 | 63 | ### Colours 64 | 65 | Although we're English, use the word `color` where possible (Over `colour`). 66 | 67 | All brand colours are specified using `primary`, `secondary` etc hierarchy. `500` is the base colour, with numbers lower (E.g. `300`) being lighter colours, and numbers (E.g. `800`) higher being darker colours. 68 | 69 | ### CSS Blocks 70 | 71 | Use BEM (http://getbem.com) style of naming when creating bespoke blocks But try to use `@apply` where possible to use existing CSS rules generated by Tailwind. 72 | 73 | Example: 74 | 75 | ``` 76 | .card 77 | .card__content 78 | .card__content--large 79 | ``` 80 | 81 | ### CSS Components 82 | 83 | CSS Components are styles that are used for repeat elements throughout a project. They are created in a separate component file in `assets/scss/components`. Try to avoid creating components where possible and use utility classes. 84 | 85 | Name | Base Class | File | Description 86 | --- | --- | --- | --- 87 | Button | `.button` | `button.scss` | Applies bases styles for buttons such as hovers and transition. 88 | Form | `.form` | `form.scss` | Applies base styles for form elements (Fields, Labels etc) 89 | Heading | `.heading` | `headings.scss` | Used to apply base styles to headings (Especially used with `.rich-text`). 90 | Link | `.link` | `links.scss` | Used to apply base styles to links. 91 | Rich Text | `.rich-text` | `rich-text.scss` | Applies base styles to tags used inside redactor (Rich text) fields. 92 | Section | `.section` | `section.scss` | Used to limit the max width of a site, and adds a site gutter. 93 | Transition | `.transition` | `transitions.scss` | Applies transitions classes to be used on links etc. 94 | 95 | 96 | ### CSS Helpers 97 | 98 | CSS Helpers are usually SCSS mixins or functions that can be used within components or blocks. They are created in separate helper files in `assets/scss/helpers`. If a new helper is created, or a helper is modified for a project consider submitting as a pull request to be used in future projects. 99 | 100 | Name | Property | File | Description 101 | --- | --- | --- | --- 102 | Font Face | `font-face($family, $filename, $weight, $style)` | `font-face.scss` | Allows use of custom fonts within CSS. Ideally place this within `base.scss` when being used. 103 | Hover | `hover()` | `hover.scss` | Used to detect if a browser supports `:hover` 104 | REM | `rem($size)` | `rem.scss` | Converts pixels (px) to REM values 105 | 106 | ### JS Components 107 | 108 | JS Components are chunks of functionality that do a specific function. E.g. You could have a component for creating a slideshow which includes when the slideshow loads, when it's resized etc. They are created in a separate a component file in `assets/js/components` using the following syntax: 109 | 110 | ``` 111 | var component = { 112 | var: { 113 | object: '.js-component' 114 | }, 115 | scroll: function(){}, 116 | resize: function(){}, 117 | load: function(){} 118 | } 119 | ``` 120 | 121 | When a component is required to be run add this to the `assets/js/app.js` file in the appropriate method (`load`, `resize`, `scroll`) by calling the correct method on the component e.g. `component.load();` or `component.resize()`. 122 | 123 | ### JS Naming 124 | 125 | Try to avoid linking any JS to any CSS classes or utilities to avoid any conflict. 126 | 127 | Name your JS classes the same as your components in kebab case and prefix your component classes with `.js-` so developers know this relates to a specific JS function. 128 | 129 | ### Favicons 130 | 131 | Place a file named `favicon.png` in `assets/images` to generate a favicon. Running `gulp favicon` will generate this and put the appropriate files in the `dist/images` folder. Use `gulp images` to optimise these files. 132 | 133 | ## Roadmap 134 | -------------------------------------------------------------------------------- /assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madebyshape/tailwind-css/c131d2bc030d70b88e2f8d71bd150634eb82407b/assets/images/favicon.png -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | hidden: 'is-hidden', 3 | visible: 'is-visible', 4 | selected: 'is-selected', 5 | active: 'is-active', 6 | loading: 'is-loading' 7 | } 8 | 9 | window.addEventListener( 10 | 'load', 11 | function() { 12 | // e.g. component.init(); 13 | } 14 | ); 15 | 16 | window.addEventListener( 17 | 'resize', 18 | function() { 19 | // e.g. component.resize(); 20 | } 21 | ); 22 | 23 | window.addEventListener( 24 | 'scroll', 25 | function() { 26 | // e.g. component.scroll(); 27 | } 28 | ); -------------------------------------------------------------------------------- /assets/js/components/component.js: -------------------------------------------------------------------------------- 1 | var component = { 2 | var: { 3 | object: '.js-component' 4 | }, 5 | scroll: function(){}, 6 | resize: function(){}, 7 | init: function(){} 8 | } 9 | -------------------------------------------------------------------------------- /assets/js/modernizr.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * modernizr v3.11.0 3 | * Build https://modernizr.com/download?-supports-addtest-printshiv-setclasses-testprop-dontmin 4 | * 5 | * Copyright (c) 6 | * Faruk Ates 7 | * Paul Irish 8 | * Alex Sexton 9 | * Ryan Seddon 10 | * Patrick Kettner 11 | * Stu Cox 12 | * Richard Herrera 13 | * Veeck 14 | 15 | * MIT License 16 | */ 17 | 18 | /* 19 | * Modernizr tests which native CSS3 and HTML5 features are available in the 20 | * current UA and makes the results available to you in two ways: as properties on 21 | * a global `Modernizr` object, and as classes on the `` element. This 22 | * information allows you to progressively enhance your pages with a granular level 23 | * of control over the experience. 24 | */ 25 | 26 | ;(function(scriptGlobalObject, window, document, undefined){ 27 | 28 | var tests = []; 29 | 30 | 31 | /** 32 | * ModernizrProto is the constructor for Modernizr 33 | * 34 | * @class 35 | * @access public 36 | */ 37 | var ModernizrProto = { 38 | // The current version, dummy 39 | _version: '3.11.0', 40 | 41 | // Any settings that don't work as separate modules 42 | // can go in here as configuration. 43 | _config: { 44 | 'classPrefix': '', 45 | 'enableClasses': true, 46 | 'enableJSClass': true, 47 | 'usePrefixes': true 48 | }, 49 | 50 | // Queue of tests 51 | _q: [], 52 | 53 | // Stub these for people who are listening 54 | on: function(test, cb) { 55 | // I don't really think people should do this, but we can 56 | // safe guard it a bit. 57 | // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 58 | // This is in case people listen to synchronous tests. I would leave it out, 59 | // but the code to *disallow* sync tests in the real version of this 60 | // function is actually larger than this. 61 | var self = this; 62 | setTimeout(function() { 63 | cb(self[test]); 64 | }, 0); 65 | }, 66 | 67 | addTest: function(name, fn, options) { 68 | tests.push({name: name, fn: fn, options: options}); 69 | }, 70 | 71 | addAsyncTest: function(fn) { 72 | tests.push({name: null, fn: fn}); 73 | } 74 | }; 75 | 76 | 77 | 78 | // Fake some of Object.create so we can force non test results to be non "own" properties. 79 | var Modernizr = function() {}; 80 | Modernizr.prototype = ModernizrProto; 81 | 82 | // Leak modernizr globally when you `require` it rather than force it here. 83 | // Overwrite name so constructor name is nicer :D 84 | Modernizr = new Modernizr(); 85 | 86 | 87 | 88 | var classes = []; 89 | 90 | 91 | /** 92 | * is returns a boolean if the typeof an obj is exactly type. 93 | * 94 | * @access private 95 | * @function is 96 | * @param {*} obj - A thing we want to check the type of 97 | * @param {string} type - A string to compare the typeof against 98 | * @returns {boolean} true if the typeof the first parameter is exactly the specified type, false otherwise 99 | */ 100 | function is(obj, type) { 101 | return typeof obj === type; 102 | } 103 | 104 | ; 105 | 106 | /** 107 | * Run through all tests and detect their support in the current UA. 108 | * 109 | * @access private 110 | * @returns {void} 111 | */ 112 | function testRunner() { 113 | var featureNames; 114 | var feature; 115 | var aliasIdx; 116 | var result; 117 | var nameIdx; 118 | var featureName; 119 | var featureNameSplit; 120 | 121 | for (var featureIdx in tests) { 122 | if (tests.hasOwnProperty(featureIdx)) { 123 | featureNames = []; 124 | feature = tests[featureIdx]; 125 | // run the test, throw the return value into the Modernizr, 126 | // then based on that boolean, define an appropriate className 127 | // and push it into an array of classes we'll join later. 128 | // 129 | // If there is no name, it's an 'async' test that is run, 130 | // but not directly added to the object. That should 131 | // be done with a post-run addTest call. 132 | if (feature.name) { 133 | featureNames.push(feature.name.toLowerCase()); 134 | 135 | if (feature.options && feature.options.aliases && feature.options.aliases.length) { 136 | // Add all the aliases into the names list 137 | for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 138 | featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 139 | } 140 | } 141 | } 142 | 143 | // Run the test, or use the raw value if it's not a function 144 | result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 145 | 146 | // Set each of the names on the Modernizr object 147 | for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 148 | featureName = featureNames[nameIdx]; 149 | // Support dot properties as sub tests. We don't do checking to make sure 150 | // that the implied parent tests have been added. You must call them in 151 | // order (either in the test, or make the parent test a dependency). 152 | // 153 | // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 154 | // hashtag famous last words 155 | featureNameSplit = featureName.split('.'); 156 | 157 | if (featureNameSplit.length === 1) { 158 | Modernizr[featureNameSplit[0]] = result; 159 | } else { 160 | // cast to a Boolean, if not one already or if it doesnt exist yet (like inputtypes) 161 | if (!Modernizr[featureNameSplit[0]] || Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 162 | Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 163 | } 164 | 165 | Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 166 | } 167 | 168 | classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 169 | } 170 | } 171 | } 172 | } 173 | ; 174 | 175 | /** 176 | * docElement is a convenience wrapper to grab the root element of the document 177 | * 178 | * @access private 179 | * @returns {HTMLElement|SVGElement} The root element of the document 180 | */ 181 | var docElement = document.documentElement; 182 | 183 | 184 | /** 185 | * A convenience helper to check if the document we are running in is an SVG document 186 | * 187 | * @access private 188 | * @returns {boolean} 189 | */ 190 | var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 191 | 192 | 193 | 194 | /** 195 | * setClasses takes an array of class names and adds them to the root element 196 | * 197 | * @access private 198 | * @function setClasses 199 | * @param {string[]} classes - Array of class names 200 | */ 201 | // Pass in an and array of class names, e.g.: 202 | // ['no-webp', 'borderradius', ...] 203 | function setClasses(classes) { 204 | var className = docElement.className; 205 | var classPrefix = Modernizr._config.classPrefix || ''; 206 | 207 | if (isSVG) { 208 | className = className.baseVal; 209 | } 210 | 211 | // Change `no-js` to `js` (independently of the `enableClasses` option) 212 | // Handle classPrefix on this too 213 | if (Modernizr._config.enableJSClass) { 214 | var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 215 | className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 216 | } 217 | 218 | if (Modernizr._config.enableClasses) { 219 | // Add the new classes 220 | if (classes.length > 0) { 221 | className += ' ' + classPrefix + classes.join(' ' + classPrefix); 222 | } 223 | if (isSVG) { 224 | docElement.className.baseVal = className; 225 | } else { 226 | docElement.className = className; 227 | } 228 | } 229 | } 230 | 231 | ; 232 | 233 | /** 234 | * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support 235 | * 236 | * @author kangax 237 | * @access private 238 | * @function hasOwnProp 239 | * @param {object} object - The object to check for a property 240 | * @param {string} property - The property to check for 241 | * @returns {boolean} 242 | */ 243 | 244 | // hasOwnProperty shim by kangax needed for Safari 2.0 support 245 | var hasOwnProp; 246 | 247 | (function() { 248 | var _hasOwnProperty = ({}).hasOwnProperty; 249 | /* istanbul ignore else */ 250 | /* we have no way of testing IE 5.5 or safari 2, 251 | * so just assume the else gets hit */ 252 | if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) { 253 | hasOwnProp = function(object, property) { 254 | return _hasOwnProperty.call(object, property); 255 | }; 256 | } 257 | else { 258 | hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ 259 | return ((property in object) && is(object.constructor.prototype[property], 'undefined')); 260 | }; 261 | } 262 | })(); 263 | 264 | 265 | 266 | 267 | // _l tracks listeners for async tests, as well as tests that execute after the initial run 268 | ModernizrProto._l = {}; 269 | 270 | /** 271 | * Modernizr.on is a way to listen for the completion of async tests. Being 272 | * asynchronous, they may not finish before your scripts run. As a result you 273 | * will get a possibly false negative `undefined` value. 274 | * 275 | * @memberOf Modernizr 276 | * @name Modernizr.on 277 | * @access public 278 | * @function on 279 | * @param {string} feature - String name of the feature detect 280 | * @param {Function} cb - Callback function returning a Boolean - true if feature is supported, false if not 281 | * @returns {void} 282 | * @example 283 | * 284 | * ```js 285 | * Modernizr.on('flash', function( result ) { 286 | * if (result) { 287 | * // the browser has flash 288 | * } else { 289 | * // the browser does not have flash 290 | * } 291 | * }); 292 | * ``` 293 | */ 294 | ModernizrProto.on = function(feature, cb) { 295 | // Create the list of listeners if it doesn't exist 296 | if (!this._l[feature]) { 297 | this._l[feature] = []; 298 | } 299 | 300 | // Push this test on to the listener list 301 | this._l[feature].push(cb); 302 | 303 | // If it's already been resolved, trigger it on next tick 304 | if (Modernizr.hasOwnProperty(feature)) { 305 | // Next Tick 306 | setTimeout(function() { 307 | Modernizr._trigger(feature, Modernizr[feature]); 308 | }, 0); 309 | } 310 | }; 311 | 312 | /** 313 | * _trigger is the private function used to signal test completion and run any 314 | * callbacks registered through [Modernizr.on](#modernizr-on) 315 | * 316 | * @memberOf Modernizr 317 | * @name Modernizr._trigger 318 | * @access private 319 | * @function _trigger 320 | * @param {string} feature - string name of the feature detect 321 | * @param {Function|boolean} [res] - A feature detection function, or the boolean = 322 | * result of a feature detection function 323 | * @returns {void} 324 | */ 325 | ModernizrProto._trigger = function(feature, res) { 326 | if (!this._l[feature]) { 327 | return; 328 | } 329 | 330 | var cbs = this._l[feature]; 331 | 332 | // Force async 333 | setTimeout(function() { 334 | var i, cb; 335 | for (i = 0; i < cbs.length; i++) { 336 | cb = cbs[i]; 337 | cb(res); 338 | } 339 | }, 0); 340 | 341 | // Don't trigger these again 342 | delete this._l[feature]; 343 | }; 344 | 345 | /** 346 | * addTest allows you to define your own feature detects that are not currently 347 | * included in Modernizr (under the covers it's the exact same code Modernizr 348 | * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). 349 | * Just like the official detects, the result 350 | * will be added onto the Modernizr object, as well as an appropriate className set on 351 | * the html element when configured to do so 352 | * 353 | * @memberOf Modernizr 354 | * @name Modernizr.addTest 355 | * @optionName Modernizr.addTest() 356 | * @optionProp addTest 357 | * @access public 358 | * @function addTest 359 | * @param {string|object} feature - The string name of the feature detect, or an 360 | * object of feature detect names and test 361 | * @param {Function|boolean} test - Function returning true if feature is supported, 362 | * false if not. Otherwise a boolean representing the results of a feature detection 363 | * @returns {object} the Modernizr object to allow chaining 364 | * @example 365 | * 366 | * The most common way of creating your own feature detects is by calling 367 | * `Modernizr.addTest` with a string (preferably just lowercase, without any 368 | * punctuation), and a function you want executed that will return a boolean result 369 | * 370 | * ```js 371 | * Modernizr.addTest('itsTuesday', function() { 372 | * var d = new Date(); 373 | * return d.getDay() === 2; 374 | * }); 375 | * ``` 376 | * 377 | * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday, 378 | * and to `false` every other day of the week. One thing to notice is that the names of 379 | * feature detect functions are always lowercased when added to the Modernizr object. That 380 | * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will. 381 | * 382 | * 383 | * Since we only look at the returned value from any feature detection function, 384 | * you do not need to actually use a function. For simple detections, just passing 385 | * in a statement that will return a boolean value works just fine. 386 | * 387 | * ```js 388 | * Modernizr.addTest('hasjquery', 'jQuery' in window); 389 | * ``` 390 | * 391 | * Just like before, when the above runs `Modernizr.hasjquery` will be true if 392 | * jQuery has been included on the page. Not using a function saves a small amount 393 | * of overhead for the browser, as well as making your code much more readable. 394 | * 395 | * Finally, you also have the ability to pass in an object of feature names and 396 | * their tests. This is handy if you want to add multiple detections in one go. 397 | * The keys should always be a string, and the value can be either a boolean or 398 | * function that returns a boolean. 399 | * 400 | * ```js 401 | * var detects = { 402 | * 'hasjquery': 'jQuery' in window, 403 | * 'itstuesday': function() { 404 | * var d = new Date(); 405 | * return d.getDay() === 2; 406 | * } 407 | * } 408 | * 409 | * Modernizr.addTest(detects); 410 | * ``` 411 | * 412 | * There is really no difference between the first methods and this one, it is 413 | * just a convenience to let you write more readable code. 414 | */ 415 | function addTest(feature, test) { 416 | 417 | if (typeof feature === 'object') { 418 | for (var key in feature) { 419 | if (hasOwnProp(feature, key)) { 420 | addTest(key, feature[ key ]); 421 | } 422 | } 423 | } else { 424 | 425 | feature = feature.toLowerCase(); 426 | var featureNameSplit = feature.split('.'); 427 | var last = Modernizr[featureNameSplit[0]]; 428 | 429 | // Again, we don't check for parent test existence. Get that right, though. 430 | if (featureNameSplit.length === 2) { 431 | last = last[featureNameSplit[1]]; 432 | } 433 | 434 | if (typeof last !== 'undefined') { 435 | // we're going to quit if you're trying to overwrite an existing test 436 | // if we were to allow it, we'd do this: 437 | // var re = new RegExp("\\b(no-)?" + feature + "\\b"); 438 | // docElement.className = docElement.className.replace( re, '' ); 439 | // but, no rly, stuff 'em. 440 | return Modernizr; 441 | } 442 | 443 | test = typeof test === 'function' ? test() : test; 444 | 445 | // Set the value (this is the magic, right here). 446 | if (featureNameSplit.length === 1) { 447 | Modernizr[featureNameSplit[0]] = test; 448 | } else { 449 | // cast to a Boolean, if not one already 450 | if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 451 | Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 452 | } 453 | 454 | Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test; 455 | } 456 | 457 | // Set a single class (either `feature` or `no-feature`) 458 | setClasses([(!!test && test !== false ? '' : 'no-') + featureNameSplit.join('-')]); 459 | 460 | // Trigger the event 461 | Modernizr._trigger(feature, test); 462 | } 463 | 464 | return Modernizr; // allow chaining. 465 | } 466 | 467 | // After all the tests are run, add self to the Modernizr prototype 468 | Modernizr._q.push(function() { 469 | ModernizrProto.addTest = addTest; 470 | }); 471 | 472 | 473 | 474 | 475 | /** 476 | * @optionName html5printshiv 477 | * @optionProp html5printshiv 478 | */ 479 | 480 | // Take the html5 variable out of the html5shiv scope so we can return it. 481 | var html5; 482 | if (!isSVG) { 483 | /** 484 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 485 | */ 486 | ;(function(window, document) { 487 | /*jshint evil:true */ 488 | /** version */ 489 | var version = '3.7.3'; 490 | 491 | /** Preset options */ 492 | var options = window.html5 || {}; 493 | 494 | /** Used to skip problem elements */ 495 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 496 | 497 | /** Not all elements can be cloned in IE **/ 498 | 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; 499 | 500 | /** Detect whether the browser supports default html5 styles */ 501 | var supportsHtml5Styles; 502 | 503 | /** Name of the expando, to work with multiple documents or to re-shiv one document */ 504 | var expando = '_html5shiv'; 505 | 506 | /** The id for the the documents expando */ 507 | var expanID = 0; 508 | 509 | /** Cached data for each document */ 510 | var expandoData = {}; 511 | 512 | /** Detect whether the browser supports unknown elements */ 513 | var supportsUnknownElements; 514 | 515 | (function() { 516 | try { 517 | var a = document.createElement('a'); 518 | a.innerHTML = ''; 519 | //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles 520 | supportsHtml5Styles = ('hidden' in a); 521 | 522 | supportsUnknownElements = a.childNodes.length == 1 || (function() { 523 | // assign a false positive if unable to shiv 524 | (document.createElement)('a'); 525 | var frag = document.createDocumentFragment(); 526 | return ( 527 | typeof frag.cloneNode == 'undefined' || 528 | typeof frag.createDocumentFragment == 'undefined' || 529 | typeof frag.createElement == 'undefined' 530 | ); 531 | }()); 532 | } catch(e) { 533 | // assign a false positive if detection fails => unable to shiv 534 | supportsHtml5Styles = true; 535 | supportsUnknownElements = true; 536 | } 537 | 538 | }()); 539 | 540 | /*--------------------------------------------------------------------------*/ 541 | 542 | /** 543 | * Creates a style sheet with the given CSS text and adds it to the document. 544 | * @private 545 | * @param {Document} ownerDocument The document. 546 | * @param {String} cssText The CSS text. 547 | * @returns {StyleSheet} The style element. 548 | */ 549 | function addStyleSheet(ownerDocument, cssText) { 550 | var p = ownerDocument.createElement('p'), 551 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 552 | 553 | p.innerHTML = 'x'; 554 | return parent.insertBefore(p.lastChild, parent.firstChild); 555 | } 556 | 557 | /** 558 | * Returns the value of `html5.elements` as an array. 559 | * @private 560 | * @returns {Array} An array of shived element node names. 561 | */ 562 | function getElements() { 563 | var elements = html5.elements; 564 | return typeof elements == 'string' ? elements.split(' ') : elements; 565 | } 566 | 567 | /** 568 | * Extends the built-in list of html5 elements 569 | * @memberOf html5 570 | * @param {String|Array} newElements whitespace separated list or array of new element names to shiv 571 | * @param {Document} ownerDocument The context document. 572 | */ 573 | function addElements(newElements, ownerDocument) { 574 | var elements = html5.elements; 575 | if(typeof elements != 'string'){ 576 | elements = elements.join(' '); 577 | } 578 | if(typeof newElements != 'string'){ 579 | newElements = newElements.join(' '); 580 | } 581 | html5.elements = elements +' '+ newElements; 582 | shivDocument(ownerDocument); 583 | } 584 | 585 | /** 586 | * Returns the data associated to the given document 587 | * @private 588 | * @param {Document} ownerDocument The document. 589 | * @returns {Object} An object of data. 590 | */ 591 | function getExpandoData(ownerDocument) { 592 | var data = expandoData[ownerDocument[expando]]; 593 | if (!data) { 594 | data = {}; 595 | expanID++; 596 | ownerDocument[expando] = expanID; 597 | expandoData[expanID] = data; 598 | } 599 | return data; 600 | } 601 | 602 | /** 603 | * returns a shived element for the given nodeName and document 604 | * @memberOf html5 605 | * @param {String} nodeName name of the element 606 | * @param {Document} ownerDocument The context document. 607 | * @returns {Object} The shived element. 608 | */ 609 | function createElement(nodeName, ownerDocument, data){ 610 | if (!ownerDocument) { 611 | ownerDocument = document; 612 | } 613 | if(supportsUnknownElements){ 614 | return ownerDocument.createElement(nodeName); 615 | } 616 | if (!data) { 617 | data = getExpandoData(ownerDocument); 618 | } 619 | var node; 620 | 621 | if (data.cache[nodeName]) { 622 | node = data.cache[nodeName].cloneNode(); 623 | } else if (saveClones.test(nodeName)) { 624 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 625 | } else { 626 | node = data.createElem(nodeName); 627 | } 628 | 629 | // Avoid adding some elements to fragments in IE < 9 because 630 | // * Attributes like `name` or `type` cannot be set/changed once an element 631 | // is inserted into a document/fragment 632 | // * Link elements with `src` attributes that are inaccessible, as with 633 | // a 403 response, will cause the tab/window to crash 634 | // * Script elements appended to fragments will execute when their `src` 635 | // or `text` property is set 636 | return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; 637 | } 638 | 639 | /** 640 | * returns a shived DocumentFragment for the given document 641 | * @memberOf html5 642 | * @param {Document} ownerDocument The context document. 643 | * @returns {Object} The shived DocumentFragment. 644 | */ 645 | function createDocumentFragment(ownerDocument, data){ 646 | if (!ownerDocument) { 647 | ownerDocument = document; 648 | } 649 | if(supportsUnknownElements){ 650 | return ownerDocument.createDocumentFragment(); 651 | } 652 | data = data || getExpandoData(ownerDocument); 653 | var clone = data.frag.cloneNode(), 654 | i = 0, 655 | elems = getElements(), 656 | l = elems.length; 657 | for(;i+~])(' + getElements().join('|') + ')(?=[[\\s,>+~#.:]|$)', 'gi'), 884 | replacement = '$1' + shivNamespace + '\\:$2'; 885 | 886 | while (index--) { 887 | pair = parts[index] = parts[index].split('}'); 888 | pair[pair.length - 1] = pair[pair.length - 1].replace(reElements, replacement); 889 | parts[index] = pair.join('}'); 890 | } 891 | return parts.join('{'); 892 | } 893 | 894 | /** 895 | * Removes the given wrappers, leaving the original elements. 896 | * @private 897 | * @params {Array} wrappers An array of printable wrappers. 898 | */ 899 | function removeWrappers(wrappers) { 900 | var index = wrappers.length; 901 | while (index--) { 902 | wrappers[index].removeNode(); 903 | } 904 | } 905 | 906 | /*--------------------------------------------------------------------------*/ 907 | 908 | /** 909 | * Shivs the given document for print. 910 | * @memberOf html5 911 | * @param {Document} ownerDocument The document to shiv. 912 | * @returns {Document} The shived document. 913 | */ 914 | function shivPrint(ownerDocument) { 915 | var shivedSheet, 916 | wrappers, 917 | data = getExpandoData(ownerDocument), 918 | namespaces = ownerDocument.namespaces, 919 | ownerWindow = ownerDocument.parentWindow; 920 | 921 | if (!supportsShivableSheets || ownerDocument.printShived) { 922 | return ownerDocument; 923 | } 924 | if (typeof namespaces[shivNamespace] == 'undefined') { 925 | namespaces.add(shivNamespace); 926 | } 927 | 928 | function removeSheet() { 929 | clearTimeout(data._removeSheetTimer); 930 | if (shivedSheet) { 931 | shivedSheet.removeNode(true); 932 | } 933 | shivedSheet= null; 934 | } 935 | 936 | ownerWindow.attachEvent('onbeforeprint', function() { 937 | 938 | removeSheet(); 939 | 940 | var imports, 941 | length, 942 | sheet, 943 | collection = ownerDocument.styleSheets, 944 | cssText = [], 945 | index = collection.length, 946 | sheets = Array(index); 947 | 948 | // convert styleSheets collection to an array 949 | while (index--) { 950 | sheets[index] = collection[index]; 951 | } 952 | // concat all style sheet CSS text 953 | while ((sheet = sheets.pop())) { 954 | // IE does not enforce a same origin policy for external style sheets... 955 | // but has trouble with some dynamically created stylesheets 956 | if (!sheet.disabled && reMedia.test(sheet.media)) { 957 | 958 | try { 959 | imports = sheet.imports; 960 | length = imports.length; 961 | } catch(er){ 962 | length = 0; 963 | } 964 | 965 | for (index = 0; index < length; index++) { 966 | sheets.push(imports[index]); 967 | } 968 | 969 | try { 970 | cssText.push(sheet.cssText); 971 | } catch(er){} 972 | } 973 | } 974 | 975 | // wrap all HTML5 elements with printable elements and add the shived style sheet 976 | cssText = shivCssText(cssText.reverse().join('')); 977 | wrappers = addWrappers(ownerDocument); 978 | shivedSheet = addStyleSheet(ownerDocument, cssText); 979 | 980 | }); 981 | 982 | ownerWindow.attachEvent('onafterprint', function() { 983 | // remove wrappers, leaving the original elements, and remove the shived style sheet 984 | removeWrappers(wrappers); 985 | clearTimeout(data._removeSheetTimer); 986 | data._removeSheetTimer = setTimeout(removeSheet, 500); 987 | }); 988 | 989 | ownerDocument.printShived = true; 990 | return ownerDocument; 991 | } 992 | 993 | /*--------------------------------------------------------------------------*/ 994 | 995 | // expose API 996 | html5.type += ' print'; 997 | html5.shivPrint = shivPrint; 998 | 999 | // shiv for print 1000 | shivPrint(document); 1001 | 1002 | if(typeof module == 'object' && module.exports){ 1003 | module.exports = html5; 1004 | } 1005 | 1006 | }(typeof window !== "undefined" ? window : this, document)); 1007 | } 1008 | 1009 | ; 1010 | 1011 | /** 1012 | * contains checks to see if a string contains another string 1013 | * 1014 | * @access private 1015 | * @function contains 1016 | * @param {string} str - The string we want to check for substrings 1017 | * @param {string} substr - The substring we want to search the first string for 1018 | * @returns {boolean} true if and only if the first string 'str' contains the second string 'substr' 1019 | */ 1020 | function contains(str, substr) { 1021 | return !!~('' + str).indexOf(substr); 1022 | } 1023 | 1024 | ; 1025 | 1026 | /** 1027 | * createElement is a convenience wrapper around document.createElement. Since we 1028 | * use createElement all over the place, this allows for (slightly) smaller code 1029 | * as well as abstracting away issues with creating elements in contexts other than 1030 | * HTML documents (e.g. SVG documents). 1031 | * 1032 | * @access private 1033 | * @function createElement 1034 | * @returns {HTMLElement|SVGElement} An HTML or SVG element 1035 | */ 1036 | function createElement() { 1037 | if (typeof document.createElement !== 'function') { 1038 | // This is the case in IE7, where the type of createElement is "object". 1039 | // For this reason, we cannot call apply() as Object is not a Function. 1040 | return document.createElement(arguments[0]); 1041 | } else if (isSVG) { 1042 | return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 1043 | } else { 1044 | return document.createElement.apply(document, arguments); 1045 | } 1046 | } 1047 | 1048 | ; 1049 | 1050 | /** 1051 | * Create our "modernizr" element that we do most feature tests on. 1052 | * 1053 | * @access private 1054 | */ 1055 | var modElem = { 1056 | elem: createElement('modernizr') 1057 | }; 1058 | 1059 | // Clean up this element 1060 | Modernizr._q.push(function() { 1061 | delete modElem.elem; 1062 | }); 1063 | 1064 | 1065 | 1066 | var mStyle = { 1067 | style: modElem.elem.style 1068 | }; 1069 | 1070 | // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 1071 | // the front of the queue. 1072 | Modernizr._q.unshift(function() { 1073 | delete mStyle.style; 1074 | }); 1075 | 1076 | 1077 | 1078 | /** 1079 | * getBody returns the body of a document, or an element that can stand in for 1080 | * the body if a real body does not exist 1081 | * 1082 | * @access private 1083 | * @function getBody 1084 | * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 1085 | * artificially created element that stands in for the body 1086 | */ 1087 | function getBody() { 1088 | // After page load injecting a fake body doesn't work so check if body exists 1089 | var body = document.body; 1090 | 1091 | if (!body) { 1092 | // Can't use the real body create a fake one. 1093 | body = createElement(isSVG ? 'svg' : 'body'); 1094 | body.fake = true; 1095 | } 1096 | 1097 | return body; 1098 | } 1099 | 1100 | ; 1101 | 1102 | /** 1103 | * injectElementWithStyles injects an element with style element and some CSS rules 1104 | * 1105 | * @access private 1106 | * @function injectElementWithStyles 1107 | * @param {string} rule - String representing a css rule 1108 | * @param {Function} callback - A function that is used to test the injected element 1109 | * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 1110 | * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 1111 | * @returns {boolean} the result of the specified callback test 1112 | */ 1113 | function injectElementWithStyles(rule, callback, nodes, testnames) { 1114 | var mod = 'modernizr'; 1115 | var style; 1116 | var ret; 1117 | var node; 1118 | var docOverflow; 1119 | var div = createElement('div'); 1120 | var body = getBody(); 1121 | 1122 | if (parseInt(nodes, 10)) { 1123 | // In order not to give false positives we create a node for each test 1124 | // This also allows the method to scale for unspecified uses 1125 | while (nodes--) { 1126 | node = createElement('div'); 1127 | node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 1128 | div.appendChild(node); 1129 | } 1130 | } 1131 | 1132 | style = createElement('style'); 1133 | style.type = 'text/css'; 1134 | style.id = 's' + mod; 1135 | 1136 | // 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. 1137 | // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 1138 | (!body.fake ? div : body).appendChild(style); 1139 | body.appendChild(div); 1140 | 1141 | if (style.styleSheet) { 1142 | style.styleSheet.cssText = rule; 1143 | } else { 1144 | style.appendChild(document.createTextNode(rule)); 1145 | } 1146 | div.id = mod; 1147 | 1148 | if (body.fake) { 1149 | //avoid crashing IE8, if background image is used 1150 | body.style.background = ''; 1151 | //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 1152 | body.style.overflow = 'hidden'; 1153 | docOverflow = docElement.style.overflow; 1154 | docElement.style.overflow = 'hidden'; 1155 | docElement.appendChild(body); 1156 | } 1157 | 1158 | ret = callback(div, rule); 1159 | // If this is done after page load we don't want to remove the body so check if body exists 1160 | if (body.fake) { 1161 | body.parentNode.removeChild(body); 1162 | docElement.style.overflow = docOverflow; 1163 | // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 1164 | // eslint-disable-next-line 1165 | docElement.offsetHeight; 1166 | } else { 1167 | div.parentNode.removeChild(div); 1168 | } 1169 | 1170 | return !!ret; 1171 | } 1172 | 1173 | ; 1174 | 1175 | /** 1176 | * domToCSS takes a camelCase string and converts it to kebab-case 1177 | * e.g. boxSizing -> box-sizing 1178 | * 1179 | * @access private 1180 | * @function domToCSS 1181 | * @param {string} name - String name of camelCase prop we want to convert 1182 | * @returns {string} The kebab-case version of the supplied name 1183 | */ 1184 | function domToCSS(name) { 1185 | return name.replace(/([A-Z])/g, function(str, m1) { 1186 | return '-' + m1.toLowerCase(); 1187 | }).replace(/^ms-/, '-ms-'); 1188 | } 1189 | 1190 | ; 1191 | 1192 | 1193 | /** 1194 | * wrapper around getComputedStyle, to fix issues with Firefox returning null when 1195 | * called inside of a hidden iframe 1196 | * 1197 | * @access private 1198 | * @function computedStyle 1199 | * @param {HTMLElement|SVGElement} elem - The element we want to find the computed styles of 1200 | * @param {string|null} [pseudo] - An optional pseudo element selector (e.g. :before), of null if none 1201 | * @param {string} prop - A CSS property 1202 | * @returns {CSSStyleDeclaration} the value of the specified CSS property 1203 | */ 1204 | function computedStyle(elem, pseudo, prop) { 1205 | var result; 1206 | 1207 | if ('getComputedStyle' in window) { 1208 | result = getComputedStyle.call(window, elem, pseudo); 1209 | var console = window.console; 1210 | 1211 | if (result !== null) { 1212 | if (prop) { 1213 | result = result.getPropertyValue(prop); 1214 | } 1215 | } else { 1216 | if (console) { 1217 | var method = console.error ? 'error' : 'log'; 1218 | console[method].call(console, 'getComputedStyle returning null, its possible modernizr test results are inaccurate'); 1219 | } 1220 | } 1221 | } else { 1222 | result = !pseudo && elem.currentStyle && elem.currentStyle[prop]; 1223 | } 1224 | 1225 | return result; 1226 | } 1227 | 1228 | ; 1229 | 1230 | /** 1231 | * nativeTestProps allows for us to use native feature detection functionality if available. 1232 | * some prefixed form, or false, in the case of an unsupported rule 1233 | * 1234 | * @access private 1235 | * @function nativeTestProps 1236 | * @param {Array} props - An array of property names 1237 | * @param {string} value - A string representing the value we want to check via @supports 1238 | * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 1239 | */ 1240 | // Accepts a list of property names and a single value 1241 | // Returns `undefined` if native detection not available 1242 | function nativeTestProps(props, value) { 1243 | var i = props.length; 1244 | // Start with the JS API: https://www.w3.org/TR/css3-conditional/#the-css-interface 1245 | if ('CSS' in window && 'supports' in window.CSS) { 1246 | // Try every prefixed variant of the property 1247 | while (i--) { 1248 | if (window.CSS.supports(domToCSS(props[i]), value)) { 1249 | return true; 1250 | } 1251 | } 1252 | return false; 1253 | } 1254 | // Otherwise fall back to at-rule (for Opera 12.x) 1255 | else if ('CSSSupportsRule' in window) { 1256 | // Build a condition string for every prefixed variant 1257 | var conditionText = []; 1258 | while (i--) { 1259 | conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 1260 | } 1261 | conditionText = conditionText.join(' or '); 1262 | return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { 1263 | return computedStyle(node, null, 'position') === 'absolute'; 1264 | }); 1265 | } 1266 | return undefined; 1267 | } 1268 | ; 1269 | 1270 | /** 1271 | * cssToDOM takes a kebab-case string and converts it to camelCase 1272 | * e.g. box-sizing -> boxSizing 1273 | * 1274 | * @access private 1275 | * @function cssToDOM 1276 | * @param {string} name - String name of kebab-case prop we want to convert 1277 | * @returns {string} The camelCase version of the supplied name 1278 | */ 1279 | function cssToDOM(name) { 1280 | return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 1281 | return m1 + m2.toUpperCase(); 1282 | }).replace(/^-/, ''); 1283 | } 1284 | 1285 | ; 1286 | 1287 | // testProps is a generic CSS / DOM property test. 1288 | 1289 | // In testing support for a given CSS property, it's legit to test: 1290 | // `elem.style[styleName] !== undefined` 1291 | // If the property is supported it will return an empty string, 1292 | // if unsupported it will return undefined. 1293 | 1294 | // We'll take advantage of this quick test and skip setting a style 1295 | // on our modernizr element, but instead just testing undefined vs 1296 | // empty string. 1297 | 1298 | // Property names can be provided in either camelCase or kebab-case. 1299 | 1300 | function testProps(props, prefixed, value, skipValueTest) { 1301 | skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 1302 | 1303 | // Try native detect first 1304 | if (!is(value, 'undefined')) { 1305 | var result = nativeTestProps(props, value); 1306 | if (!is(result, 'undefined')) { 1307 | return result; 1308 | } 1309 | } 1310 | 1311 | // Otherwise do it properly 1312 | var afterInit, i, propsLength, prop, before; 1313 | 1314 | // If we don't have a style element, that means we're running async or after 1315 | // the core tests, so we'll need to create our own elements to use. 1316 | 1317 | // Inside of an SVG element, in certain browsers, the `style` element is only 1318 | // defined for valid tags. Therefore, if `modernizr` does not have one, we 1319 | // fall back to a less used element and hope for the best. 1320 | // For strict XHTML browsers the hardly used samp element is used. 1321 | var elems = ['modernizr', 'tspan', 'samp']; 1322 | while (!mStyle.style && elems.length) { 1323 | afterInit = true; 1324 | mStyle.modElem = createElement(elems.shift()); 1325 | mStyle.style = mStyle.modElem.style; 1326 | } 1327 | 1328 | // Delete the objects if we created them. 1329 | function cleanElems() { 1330 | if (afterInit) { 1331 | delete mStyle.style; 1332 | delete mStyle.modElem; 1333 | } 1334 | } 1335 | 1336 | propsLength = props.length; 1337 | for (i = 0; i < propsLength; i++) { 1338 | prop = props[i]; 1339 | before = mStyle.style[prop]; 1340 | 1341 | if (contains(prop, '-')) { 1342 | prop = cssToDOM(prop); 1343 | } 1344 | 1345 | if (mStyle.style[prop] !== undefined) { 1346 | 1347 | // If value to test has been passed in, do a set-and-check test. 1348 | // 0 (integer) is a valid property value, so check that `value` isn't 1349 | // undefined, rather than just checking it's truthy. 1350 | if (!skipValueTest && !is(value, 'undefined')) { 1351 | 1352 | // Needs a try catch block because of old IE. This is slow, but will 1353 | // be avoided in most cases because `skipValueTest` will be used. 1354 | try { 1355 | mStyle.style[prop] = value; 1356 | } catch (e) {} 1357 | 1358 | // If the property value has changed, we assume the value used is 1359 | // supported. If `value` is empty string, it'll fail here (because 1360 | // it hasn't changed), which matches how browsers have implemented 1361 | // CSS.supports() 1362 | if (mStyle.style[prop] !== before) { 1363 | cleanElems(); 1364 | return prefixed === 'pfx' ? prop : true; 1365 | } 1366 | } 1367 | // Otherwise just return true, or the property name if this is a 1368 | // `prefixed()` call 1369 | else { 1370 | cleanElems(); 1371 | return prefixed === 'pfx' ? prop : true; 1372 | } 1373 | } 1374 | } 1375 | cleanElems(); 1376 | return false; 1377 | } 1378 | 1379 | ; 1380 | 1381 | /** 1382 | * testProp() investigates whether a given style property is recognized 1383 | * Property names can be provided in either camelCase or kebab-case. 1384 | * 1385 | * @memberOf Modernizr 1386 | * @name Modernizr.testProp 1387 | * @access public 1388 | * @optionName Modernizr.testProp() 1389 | * @optionProp testProp 1390 | * @function testProp 1391 | * @param {string} prop - Name of the CSS property to check 1392 | * @param {string} [value] - Name of the CSS value to check 1393 | * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported 1394 | * @returns {boolean} an empty string if the property is supported, undefined if its unsupported 1395 | * @example 1396 | * 1397 | * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed 1398 | * version of the string. 1399 | * 1400 | * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) 1401 | * 1402 | * ```js 1403 | * Modernizr.testProp('pointerEvents') // true 1404 | * ``` 1405 | * 1406 | * You can also provide a value as an optional second argument to check if a 1407 | * specific value is supported 1408 | * 1409 | * ```js 1410 | * Modernizr.testProp('pointerEvents', 'none') // true 1411 | * Modernizr.testProp('pointerEvents', 'penguin') // false 1412 | * ``` 1413 | */ 1414 | var testProp = ModernizrProto.testProp = function(prop, value, useValue) { 1415 | return testProps([prop], undefined, value, useValue); 1416 | }; 1417 | 1418 | 1419 | /*! 1420 | { 1421 | "name": "CSS Supports", 1422 | "property": "supports", 1423 | "caniuse": "css-featurequeries", 1424 | "tags": ["css"], 1425 | "builderAliases": ["css_supports"], 1426 | "notes": [{ 1427 | "name": "W3C Spec (The @supports rule)", 1428 | "href": "https://dev.w3.org/csswg/css3-conditional/#at-supports" 1429 | }, { 1430 | "name": "Related Github Issue", 1431 | "href": "https://github.com/Modernizr/Modernizr/issues/648" 1432 | }, { 1433 | "name": "W3C Spec (The CSSSupportsRule interface)", 1434 | "href": "https://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface" 1435 | }] 1436 | } 1437 | !*/ 1438 | 1439 | var newSyntax = 'CSS' in window && 'supports' in window.CSS; 1440 | var oldSyntax = 'supportsCSS' in window; 1441 | Modernizr.addTest('supports', newSyntax || oldSyntax); 1442 | 1443 | 1444 | // Run each test 1445 | testRunner(); 1446 | 1447 | // Remove the "no-js" class if it exists 1448 | setClasses(classes); 1449 | 1450 | delete ModernizrProto.addTest; 1451 | delete ModernizrProto.addAsyncTest; 1452 | 1453 | // Run the things that are supposed to run after the tests 1454 | for (var i = 0; i < Modernizr._q.length; i++) { 1455 | Modernizr._q[i](); 1456 | } 1457 | 1458 | // Leak Modernizr namespace 1459 | scriptGlobalObject.Modernizr = Modernizr; 1460 | 1461 | 1462 | ; 1463 | 1464 | })(window, window, document); 1465 | -------------------------------------------------------------------------------- /assets/scss/app.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | 4 | @import 'utilities/*'; 5 | @import 'components/*'; 6 | @import 'base'; 7 | 8 | @tailwind utilities; -------------------------------------------------------------------------------- /assets/scss/base.scss: -------------------------------------------------------------------------------- 1 | // Font Face 2 | // @include font-face('family', 'filename', 500, 'normal'); 3 | 4 | // Body 5 | 6 | html { 7 | @apply text-base; 8 | } 9 | 10 | body { 11 | @apply 12 | font-sans-primary 13 | text-gray-800 14 | text-optimize-legibility 15 | antialiased; 16 | } 17 | 18 | // Links 19 | 20 | a { 21 | @apply transition--default; 22 | } 23 | 24 | // Typography 25 | 26 | p { 27 | @apply mb-4; 28 | &:last-of-type { 29 | @apply mb-0; 30 | } 31 | } 32 | 33 | // Selection 34 | 35 | ::selection { 36 | @apply 37 | text-primary-500 38 | bg-primary-100; 39 | } 40 | 41 | // FOUT fix 42 | 43 | .wf-loading { 44 | @apply invisible; 45 | } 46 | 47 | .wf-active, 48 | .wf-inactive { 49 | @apply visible; 50 | } 51 | -------------------------------------------------------------------------------- /assets/scss/components/alpinejs.scss: -------------------------------------------------------------------------------- 1 | [x-cloak] { 2 | @apply hidden; 3 | } -------------------------------------------------------------------------------- /assets/scss/components/links.scss: -------------------------------------------------------------------------------- 1 | .link { 2 | @apply transition--default; 3 | &--primary { 4 | @apply 5 | text-primary-500 6 | underline; 7 | &:hover, 8 | &:active { 9 | @apply text-primary-700; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /assets/scss/utilities/font-face.scss: -------------------------------------------------------------------------------- 1 | $font-path: '../fonts'; 2 | 3 | @mixin font-face($family, $filename, $weight, $style) { 4 | 5 | $weight: '400' !default; 6 | $style: 'normal' !default; 7 | 8 | @font-face { 9 | font-family: "#{$family}"; 10 | src: url("#{$font-path}/#{$filename}.eot"); 11 | src: local("#{$family}"), 12 | url("#{$font-path}/#{$filename}.eot?#iefix") format("embedded-opentype"), 13 | url("#{$font-path}/#{$filename}.woff2") format("woff2"), 14 | url("#{$font-path}/#{$filename}.woff") format("woff"), 15 | url("#{$font-path}/#{$filename}.ttf") format("truetype"), 16 | url("#{$font-path}/#{$filename}.svg##{$filename}") format("svg"); 17 | font-weight: $weight; 18 | font-style: $style; 19 | font-display: swap; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /assets/scss/utilities/rem.scss: -------------------------------------------------------------------------------- 1 | @function rem($size) { 2 | $remSize: $size / 16; 3 | @return #{$remSize}rem; 4 | } 5 | -------------------------------------------------------------------------------- /assets/scss/utilities/transition.scss: -------------------------------------------------------------------------------- 1 | .transition { 2 | &--default { 3 | @apply 4 | transition-all 5 | duration-500 6 | ease-out; 7 | } 8 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const package = require("./package.json"); 2 | 3 | const gulp = require("gulp"); 4 | const sass = require("gulp-sass"); 5 | const postcss = require("gulp-postcss"); 6 | const tailwindcss = require("tailwindcss"); 7 | const browsersync = require("browser-sync").create(); 8 | const sassglob = require("gulp-sass-glob"); 9 | const sourcemaps = require("gulp-sourcemaps"); 10 | const autoprefixer = require("autoprefixer"); 11 | const cssnano = require("cssnano"); 12 | const newer = require("gulp-newer"); 13 | const imagemin = require("gulp-imagemin"); 14 | const minify = require("gulp-minify"); 15 | const modernizr = require("gulp-modernizr"); 16 | const purgecss = require("gulp-purgecss"); 17 | const concat = require("gulp-concat"); 18 | const favicons = require("favicons").stream; 19 | const rev = require("gulp-rev"); 20 | const revDel = require("rev-del"); 21 | const plumber = require("gulp-plumber"); 22 | const notify = require("gulp-notify"); 23 | const del = require("del"); 24 | const critical = require("critical"); 25 | const clean = require("gulp-dest-clean"); 26 | 27 | function browserSync(done) { 28 | 29 | if (package.env.local === "") { 30 | browsersync.init({ 31 | server: { 32 | baseDir: package.paths.public 33 | } 34 | }); 35 | } 36 | else { 37 | browsersync.init({ 38 | proxy: package.env.local 39 | }); 40 | } 41 | done(); 42 | 43 | } 44 | 45 | function browserSyncReload(done) { 46 | 47 | browsersync.reload(); 48 | done(); 49 | 50 | } 51 | 52 | function css() { 53 | 54 | const cssFiles = [ 55 | package.paths.assets.scss + package.files.assets.scss 56 | ]; 57 | 58 | for (var i = 0; i < package.cssDependencies.length; i++) { 59 | cssFiles.unshift(package.paths.dependencies + package.cssDependencies[i]); 60 | } 61 | 62 | const plugins = [ 63 | tailwindcss(package.files.tailwind), 64 | autoprefixer(), 65 | cssnano() 66 | ]; 67 | 68 | return gulp 69 | .src(cssFiles) 70 | .pipe(plumber({ errorHandler: notify.onError("Error [css]: <%= error.message %>") })) 71 | .pipe(concat(package.files.dist.css)) 72 | .pipe(sourcemaps.init()) 73 | .pipe(sassglob()) 74 | .pipe(sass()) 75 | .pipe(postcss(plugins)) 76 | .pipe(sourcemaps.write("/")) 77 | .pipe(gulp.dest(package.paths.public + package.paths.dist.css)) 78 | .pipe(browsersync.stream()); 79 | 80 | } 81 | 82 | function js() { 83 | 84 | const jsArray = []; 85 | 86 | const jsFiles = [ 87 | "modernizr.js", 88 | "components/**/*.js", 89 | package.files.assets.js 90 | ]; 91 | 92 | for (var i = 0; i < package.jsDependencies.length; i++) { 93 | jsArray.push(package.paths.dependencies + package.jsDependencies[i]); 94 | } 95 | 96 | for (var i = 0; i < jsFiles.length; i++) { 97 | jsArray.push(package.paths.assets.js + jsFiles[i]); 98 | } 99 | 100 | return gulp 101 | .src(jsArray) 102 | .pipe(plumber({ errorHandler: notify.onError("Error [js]: <%= error.message %>") })) 103 | .pipe(concat(package.files.dist.js)) 104 | .pipe(sourcemaps.init()) 105 | .pipe( 106 | minify( 107 | { 108 | ext:{ 109 | min:".js" 110 | }, 111 | noSource: true 112 | } 113 | ) 114 | ) 115 | .pipe(sourcemaps.write("/")) 116 | .pipe(gulp.dest(package.paths.public + package.paths.dist.js)) 117 | .pipe(browsersync.stream()); 118 | } 119 | 120 | function images() { 121 | 122 | return gulp 123 | .src(package.paths.assets.images + "**/*") 124 | .pipe(plumber({ errorHandler: notify.onError("Error [images]: <%= error.message %>") })) 125 | .pipe(clean(package.paths.public + package.paths.dist.images, 'favicon/**')) 126 | .pipe(newer(package.paths.public + package.paths.dist.images)) 127 | .pipe( 128 | imagemin( 129 | [ 130 | imagemin.gifsicle({ interlaced: true }), 131 | imagemin.jpegtran({ progressive: true }), 132 | imagemin.optipng({ optimizationLevel: 5 }), 133 | imagemin.svgo({ 134 | plugins: [ 135 | { removeViewBox: true }, 136 | { cleanupIDs: false } 137 | ] 138 | }) 139 | ], 140 | { verbose: true } 141 | ) 142 | ) 143 | .pipe(gulp.dest(package.paths.public + package.paths.dist.images)); 144 | 145 | } 146 | 147 | function favicon() { 148 | 149 | return gulp 150 | .src(package.paths.assets.images + "favicon.{jpg,png}") 151 | .pipe(plumber({ errorHandler: notify.onError("Error [favicon]: <%= error.message %>") })) 152 | .pipe(newer(package.paths.public + package.paths.dist.images + "favicon.{jpg,png}")) 153 | .pipe( 154 | favicons( 155 | { 156 | appName: package.name, 157 | appDescription: package.description, 158 | developerName: package.author, 159 | developerURL: package.authorUrl, 160 | background: "#FFF", 161 | display: "standalone", 162 | orientation: "any", 163 | version: 1.0, 164 | html: "favicons.html", 165 | pipeHTML: true, 166 | replace: true, 167 | path: "/" + package.paths.dist.favicon, 168 | icons: { 169 | favicons: true, 170 | android: true, 171 | appleIcon: true, 172 | firefox: true, 173 | windows: true, 174 | appleStartup: false, 175 | coast: false, 176 | opengraph: false, 177 | twitter: false, 178 | yandex: false 179 | } 180 | } 181 | ) 182 | ) 183 | .pipe(gulp.dest(package.paths.public + package.paths.dist.favicon)); 184 | 185 | } 186 | 187 | function faviconHtml() { 188 | return gulp 189 | .src( 190 | package.paths.public + package.paths.dist.favicon + "favicons.html" 191 | ) 192 | .pipe( 193 | gulp.dest(package.paths.templates + "_components/") 194 | ); 195 | } 196 | 197 | function purgeCss() { 198 | 199 | class TailwindExtractor { 200 | static extract(content) { 201 | return content.match(/[A-z0-9-:\/]+/g); 202 | } 203 | } 204 | 205 | var whitelistPatterns = []; 206 | for (i = 0; i < package.purgeCss.whitelistPatterns.length; i++) { 207 | whitelistPatterns.push(new RegExp(package.purgeCss.whitelistPatterns[i], "")); 208 | } 209 | 210 | return gulp 211 | .src(package.paths.public + package.paths.dist.css + package.files.dist.css) 212 | .pipe(plumber({ errorHandler: notify.onError("Error [purgeCss]: <%= error.message %>") })) 213 | .pipe( 214 | purgecss({ 215 | content: [ 216 | package.paths.templates + "**/*.{html,twig,vue}", 217 | package.paths.public + package.paths.dist.base + "**/*.{js}" 218 | ], 219 | whitelistPatterns: whitelistPatterns, 220 | whitelistPatternsChildren: whitelistPatterns, 221 | extractors: [ 222 | { 223 | extractor: TailwindExtractor, 224 | extensions: ["html", "twig", "vue"] 225 | } 226 | ] 227 | }) 228 | ) 229 | .pipe(gulp.dest(package.paths.public + package.paths.dist.css)); 230 | 231 | } 232 | 233 | function doSynchronousLoop(data, processData, done) { 234 | 235 | if (data.length > 0) { 236 | const loop = (data, i, processData, done) => { 237 | processData(data[i], i, () => { 238 | if (++i < data.length) { 239 | loop(data, i, processData, done); 240 | } else { 241 | done(); 242 | } 243 | }); 244 | }; 245 | loop(data, 0, processData, done); 246 | } else { 247 | done(); 248 | } 249 | 250 | } 251 | 252 | const processCriticalCSS = (element, i, callback) => { 253 | 254 | critical 255 | .generate({ 256 | src: package.critical.url + element.url, 257 | dest: package.templates + element.path + element.slug + "-critical.css", 258 | inline: false, 259 | ignore: [], 260 | bbase: "./", 261 | pathPrefix: "/", 262 | css: [package.files.dist.css], 263 | width: 1400, 264 | height: 900, 265 | minify: true, 266 | timeout: 60000 267 | }, 268 | (err, output) => { 269 | if (err) { 270 | notify({ 271 | message: "Error [processCriticalCSS]: " + err 272 | }) 273 | } 274 | callback(); 275 | } 276 | ); 277 | 278 | } 279 | 280 | function criticalCss(done) { 281 | 282 | doSynchronousLoop( 283 | package.critical.elements, 284 | processCriticalCSS, 285 | () => { 286 | done(); 287 | } 288 | ); 289 | 290 | } 291 | 292 | function revCssJs(done) { 293 | 294 | return gulp 295 | .src( 296 | [ 297 | package.paths.public + package.paths.dist.css + package.files.dist.css, 298 | package.paths.public + package.paths.dist.js + package.files.dist.js 299 | ], 300 | { 301 | base: package.paths.public + package.paths.dist.base 302 | } 303 | ) 304 | .pipe(rev()) 305 | .pipe(gulp.dest(package.paths.public + package.paths.dist.base)) 306 | .pipe(rev.manifest( 307 | { 308 | base: "./" 309 | } 310 | )) 311 | .pipe(revDel( 312 | { 313 | oldManifest: "./rev-manifest.json", 314 | dest: package.paths.public + package.paths.dist.base 315 | } 316 | )) 317 | .pipe(gulp.dest("./")); 318 | 319 | } 320 | 321 | function browserFeatures() { 322 | 323 | return gulp 324 | .src(package.paths.assets.js + "**/*") 325 | .pipe(modernizr()) 326 | .pipe(gulp.dest(package.paths.assets.js)); 327 | 328 | } 329 | 330 | function watch(done) { 331 | 332 | gulp.watch( 333 | [ 334 | "package.json", 335 | package.files.tailwind, 336 | package.paths.assets.scss + "**/*", 337 | ], 338 | css 339 | ); 340 | 341 | gulp.watch( 342 | [ 343 | "package.json", 344 | package.paths.assets.js + "**/*" 345 | ], 346 | js 347 | ); 348 | 349 | gulp.watch(package.paths.public + "**/*", browserSyncReload); 350 | 351 | gulp.watch(package.paths.templates + "**/*.{html,twig,vue}", browserSyncReload); 352 | 353 | gulp.watch(package.paths.assets.images + "**/*", images); 354 | 355 | done(); 356 | 357 | } 358 | 359 | exports.browserSync = browserSync; 360 | exports.browserSyncReload = browserSyncReload; 361 | exports.css = css; 362 | exports.js = js; 363 | exports.images = images; 364 | exports.favicon = favicon; 365 | exports.faviconHtml = faviconHtml; 366 | exports.purgeCss = purgeCss; 367 | exports.criticalCss = criticalCss; 368 | exports.revCssJs = revCssJs; 369 | exports.browserFeatures = browserFeatures; 370 | exports.watch = watch; 371 | 372 | exports.dev = gulp.series(browserFeatures, css, js, images, watch, browserSync); 373 | exports.production = gulp.series( 374 | gulp.parallel(browserFeatures, css, js), 375 | purgeCss, 376 | criticalCss, 377 | revCssJs, 378 | favicon, 379 | faviconHtml, 380 | images 381 | ); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tailwind", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gulpfile.js", 6 | "env": { 7 | "local": "" 8 | }, 9 | "paths": { 10 | "dependencies": "node_modules/", 11 | "public": "web/", 12 | "templates": "templates/", 13 | "dist": { 14 | "base": "dist/", 15 | "css": "dist/css/", 16 | "js": "dist/js/", 17 | "images": "dist/images/", 18 | "favicon": "dist/images/favicon/" 19 | }, 20 | "assets": { 21 | "scss": "assets/scss/", 22 | "js": "assets/js/", 23 | "images": "assets/images/", 24 | "fonts": "assets/fonts/" 25 | } 26 | }, 27 | "files": { 28 | "tailwind": "./tailwind.config.js", 29 | "dist": { 30 | "css": "app.css", 31 | "js": "app.js" 32 | }, 33 | "assets": { 34 | "scss": "app.scss", 35 | "js": "app.js" 36 | } 37 | }, 38 | "critical": { 39 | "url": "http://tailwind-css.local/", 40 | "elements": [ 41 | { 42 | "uri": "", 43 | "slug": "", 44 | "path": "" 45 | } 46 | ] 47 | }, 48 | "purgeCss": { 49 | "whitelistPatterns": [ 50 | "is-.*", 51 | "aos.*", 52 | "choices.*", 53 | "flickity.*", 54 | "mfp.*" 55 | ] 56 | }, 57 | "cssDependencies": [], 58 | "jsDependencies": [ 59 | "fg-loadcss/dist/cssrelpreload.js", 60 | "alpinejs/dist/alpine.js" 61 | ], 62 | "dependencies": { 63 | "alpinejs": "^2.3.5", 64 | "fg-loadcss": "^2.1.0", 65 | "tailwindcss": "^1.4.6", 66 | "tailwindcss-animations": "^2.0.0", 67 | "tailwindcss-aspect-ratio": "^2.0.0", 68 | "tailwindcss-typography": "^3.1.0" 69 | }, 70 | "devDependencies": { 71 | "autoprefixer": "^9.8.0", 72 | "browser-sync": "^2.26.7", 73 | "critical": "^1.3.9", 74 | "cssnano": "^4.1.10", 75 | "del": "^4.1.1", 76 | "favicons": "^5.5.0", 77 | "gulp": "^4.0.2", 78 | "gulp-concat": "^2.6.1", 79 | "gulp-dest-clean": "^0.5.0", 80 | "gulp-imagemin": "^5.0.3", 81 | "gulp-minify": "^3.1.0", 82 | "gulp-modernizr": "^3.4.1", 83 | "gulp-newer": "^1.4.0", 84 | "gulp-notify": "^3.2.0", 85 | "gulp-plumber": "^1.2.1", 86 | "gulp-postcss": "^8.0.0", 87 | "gulp-purgecss": "^1.2.0", 88 | "gulp-rev": "^9.0.0", 89 | "gulp-sass": "^4.1.0", 90 | "gulp-sass-glob": "^1.1.0", 91 | "gulp-sourcemaps": "^2.6.5", 92 | "node-sass": "^4.14.1", 93 | "purgecss": "^1.4.2", 94 | "rev-del": "^2.0.0" 95 | }, 96 | "scripts": { 97 | "dev": "gulp dev", 98 | "production": "gulp production" 99 | }, 100 | "repository": { 101 | "type": "git", 102 | "url": "git+https://github.com/madebyshape/tailwind-css.git" 103 | }, 104 | "author": "Jason Mayo (@bymayo)", 105 | "authorUrl": "https://madebyshape.co.uk", 106 | "license": "ISC", 107 | "bugs": { 108 | "url": "https://github.com/madebyshape/tailwind-css/issues" 109 | }, 110 | "homepage": "https://github.com/madebyshape/tailwind-css#readme" 111 | } 112 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const defaultTheme = require('tailwindcss/defaultTheme'); 2 | const alpha = { 100: 'FF', 90: 'E6', 80: 'CC', 70: 'B3', 60: '99', 50: '80', 40: '66', 30: '4D', 20: '33', 10: '1A' }; 3 | 4 | module.exports = { 5 | theme: { 6 | extend: { 7 | colors: { 8 | primary: { 9 | 100: '#F0F2FD', 10 | 200: '#D9DFFA', 11 | 300: '#C2CBF7', 12 | 400: '#94A5F0', 13 | 500: '#667EEA', 14 | 600: '#5C71D3', 15 | 700: '#3D4C8C', 16 | 800: '#2E3969', 17 | 900: '#1F2646' 18 | }, 19 | secondary: { 20 | 100: '#EBF7F7', 21 | 200: '#CDECEA', 22 | 300: '#AFE0DE', 23 | 400: '#74C9C5', 24 | 500: '#38B2AC', 25 | 600: '#32A09B', 26 | 700: '#226B67', 27 | 800: '#19504D', 28 | 900: '#113534' 29 | }, 30 | brands: { 31 | facebook: '#3b5998', 32 | twitter: '#55acee', 33 | instagram: '#3f729b', 34 | linkedin: '#0976b4', 35 | youtube: '#e52d27', 36 | vimeo: '#1ab7ea', 37 | pinterest: '#cc2127' 38 | } 39 | }, 40 | fontSize: theme => ({ 41 | 'xs': ['0.75rem', theme('lineHeight.normal')], // 12 42 | 'sm': ['0.875rem', theme('lineHeight.normal')], // 14 43 | 'base': ['1rem', theme('lineHeight.normal')], // 16 44 | 'md': ['1.125rem', theme('lineHeight.normal')], // 18 45 | 'lg': ['1.25rem', theme('lineHeight.normal')], // 20 46 | 'xl': ['1.5rem', theme('lineHeight.normal')], // 24 47 | '2xl': ['1.875rem', theme('lineHeight.normal')], // 30 48 | '3xl': ['2.25rem', theme('lineHeight.normal')], // 36 49 | '4xl': ['3rem', theme('lineHeight.normal')], // 48 50 | '5xl': ['4rem', theme('lineHeight.normal')], // 64 51 | '6xl': ['4.5rem', theme('lineHeight.normal')] // 72 52 | }), 53 | fontFamily: { 54 | 'sans-primary': [ 55 | ...defaultTheme.fontFamily.sans 56 | ], 57 | 'serif-primary': [ 58 | ...defaultTheme.fontFamily.serif 59 | ], 60 | 'mono-primary': [ 61 | ...defaultTheme.fontFamily.mono 62 | ], 63 | }, 64 | boxShadow: theme => ({ 65 | 'outline': '0 0 0 3px ' + theme('colors.primary.500') + alpha[20], 66 | 'focus': '0 0 0 3px ' + theme('colors.primary.500') + alpha[20] 67 | }), 68 | container: { 69 | center: true, 70 | padding: { 71 | default: '1rem', 72 | sm: '2rem', 73 | lg: '4rem', 74 | xl: '5rem' 75 | } 76 | }, 77 | height: { 78 | 'screen-fix': 'calc(var(--vh, 1vh) * 100);' 79 | }, 80 | inset: { 81 | '0': 0, 82 | '1/2': '50%' 83 | }, 84 | // Plugins 85 | textStyles: theme => ({ 86 | richText: { 87 | fontSize: theme('fontSize.base')[0], 88 | lineHeight: theme('fontSize.base')[1], 89 | 'h1, h2, h3, h4, h5, h6': { 90 | marginBottom: theme('spacing.4') 91 | }, 92 | 'h1': { 93 | fontSize: theme('fontSize.3xl')[0], 94 | lineHeight: theme('fontSize.3xl')[1] 95 | }, 96 | 'h2': { 97 | fontSize: theme('fontSize.2xl')[0], 98 | lineHeight: theme('fontSize.2xl')[1] 99 | }, 100 | 'h3': { 101 | fontSize: theme('fontSize.xl')[0], 102 | lineHeight: theme('fontSize.xl')[1] 103 | }, 104 | 'h4': { 105 | fontSize: theme('fontSize.lg')[0], 106 | lineHeight: theme('fontSize.lg')[1] 107 | }, 108 | 'h5': { 109 | fontSize: theme('fontSize.md')[0], 110 | lineHeight: theme('fontSize.md')[1] 111 | }, 112 | 'h6': { 113 | fontSize: theme('fontSize.base')[0], 114 | lineHeight: theme('fontSize.base')[1] 115 | }, 116 | 'ul,ol': { 117 | listStylePosition: 'inside', 118 | marginBottom: theme('spacing.4') 119 | }, 120 | 'ul': { 121 | listStyleType: 'disc', 122 | }, 123 | 'ol': { 124 | listStyleType: 'decimal', 125 | }, 126 | 'a': { 127 | textDecoration: 'underline', 128 | color: theme('colors.primary.500'), 129 | '&:hover': { 130 | color: theme('colors.primary.600') 131 | } 132 | }, 133 | 'b, strong': { 134 | fontWeight: theme('fontWeight.bold'), 135 | }, 136 | 'i, em': { 137 | fontStyle: 'italic', 138 | } 139 | } 140 | }), 141 | animations: {}, 142 | aspectRatio: { 143 | 'square': [1, 1], 144 | '16/9': [16, 9], 145 | '4/3': [4, 3] 146 | } 147 | }, 148 | screens: { 149 | ...defaultTheme.screens, 150 | } 151 | }, 152 | variants: { 153 | textColor: ['responsive', 'hover', 'focus', 'group-hover'], 154 | backgroundColor: ['responsive', 'hover', 'focus', 'group-hover'], 155 | opacity: ['responsive', 'hover', 'group-hover'], 156 | translate: ['responsive', 'hover', 'group-hover'], 157 | scale: ['responsive', 'hover', 'group-hover'], 158 | animations: ['responsive', 'hover', 'group-hover'] 159 | }, 160 | plugins: [ 161 | require('tailwindcss-typography')({ componentPrefix: '' }), 162 | require('tailwindcss-aspect-ratio')(), 163 | require('tailwindcss-animations') 164 | ], 165 | purge: [] 166 | } 167 | -------------------------------------------------------------------------------- /web/dist/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madebyshape/tailwind-css/c131d2bc030d70b88e2f8d71bd150634eb82407b/web/dist/images/favicon.png -------------------------------------------------------------------------------- /web/dist/js/app.js: -------------------------------------------------------------------------------- 1 | !function(e){"use strict";e.loadCSS||(e.loadCSS=function(){});var t=loadCSS.relpreload={};if(t.support=function(){var t;try{t=e.document.createElement("link").relList.supports("preload")}catch(e){t=!1}return function(){return t}}(),t.bindMediaToggle=function(e){var t=e.media||"all";function n(){e.addEventListener?e.removeEventListener("load",n):e.attachEvent&&e.detachEvent("onload",n),e.setAttribute("onload",null),e.media=t}e.addEventListener?e.addEventListener("load",n):e.attachEvent&&e.attachEvent("onload",n),setTimeout(function(){e.rel="stylesheet",e.media="only x"}),setTimeout(n,3e3)},t.poly=function(){if(!t.support())for(var n=e.document.getElementsByTagName("link"),i=0;i{const t=u(e.name),n=t.match(a),i=t.match(/:([a-zA-Z\-:]+)/),r=t.match(/\.[^.\]]+(?=[^\]]*$)/g)||[];return{type:n?n[1]:null,value:i?i[1]:null,modifiers:r.map(e=>e.replace(".","")),expression:e.value}}).filter(e=>!t||e.type===t)}function u(e){return e.startsWith("@")?e.replace("@","x-on:"):e.startsWith(":")?e.replace(":","x-bind:"):e}function d(e,t,n=!1){if(n)return t();const i=c(e,"transition"),r=c(e,"show")[0];if(r&&r.modifiers.includes("transition")){let n=r.modifiers;if(n.includes("out")&&!n.includes("in"))return t();const i=n.includes("in")&&n.includes("out");(function(e,t,n){const i={duration:p(t,"duration",150),origin:p(t,"origin","center"),first:{opacity:0,scale:p(t,"scale",95)},second:{opacity:1,scale:100}};m(e,t,n,()=>{},i)})(e,n=i?n.filter((e,t)=>t["enter","enter-start","enter-end"].includes(e.value)).length>0?function(e,t,n){const i=(t.find(e=>"enter"===e.value)||{expression:""}).expression.split(" ").filter(e=>""!==e),r=(t.find(e=>"enter-start"===e.value)||{expression:""}).expression.split(" ").filter(e=>""!==e),o=(t.find(e=>"enter-end"===e.value)||{expression:""}).expression.split(" ").filter(e=>""!==e);h(e,i,r,o,n,()=>{})}(e,i,t):t()}function f(e,t,n=!1){if(n)return t();const i=c(e,"transition"),r=c(e,"show")[0];if(r&&r.modifiers.includes("transition")){let n=r.modifiers;if(n.includes("in")&&!n.includes("out"))return t();const i=n.includes("in")&&n.includes("out");(function(e,t,n,i){const r={duration:n?p(t,"duration",150):p(t,"duration",150)/2,origin:p(t,"origin","center"),first:{opacity:1,scale:100},second:{opacity:0,scale:p(t,"scale",95)}};m(e,t,()=>{},i,r)})(e,n=i?n.filter((e,t)=>t>n.indexOf("out")):n,i,t)}else i.filter(e=>["leave","leave-start","leave-end"].includes(e.value)).length>0?function(e,t,n){const i=(t.find(e=>"leave"===e.value)||{expression:""}).expression.split(" ").filter(e=>""!==e),r=(t.find(e=>"leave-start"===e.value)||{expression:""}).expression.split(" ").filter(e=>""!==e),o=(t.find(e=>"leave-end"===e.value)||{expression:""}).expression.split(" ").filter(e=>""!==e);h(e,i,r,o,()=>{},n)}(e,i,t):t()}function p(e,t,n){if(-1===e.indexOf(t))return n;const i=e[e.indexOf(t)+1];if(!i)return n;if("scale"===t&&!g(i))return n;if("duration"===t){let e=i.match(/([0-9]+)ms/);if(e)return e[1]}return"origin"===t&&["top","right","left","center","bottom"].includes(e[e.indexOf(t)+2])?[i,e[e.indexOf(t)+2]].join(" "):i}function m(e,t,n,i,r){const o=e.style.opacity,s=e.style.transform,a=e.style.transformOrigin,l=!t.includes("opacity")&&!t.includes("scale"),c=l||t.includes("opacity"),u=l||t.includes("scale"),d={start(){c&&(e.style.opacity=r.first.opacity),u&&(e.style.transform=`scale(${r.first.scale/100})`)},during(){u&&(e.style.transformOrigin=r.origin),e.style.transitionProperty=[c?"opacity":"",u?"transform":""].join(" ").trim(),e.style.transitionDuration=`${r.duration/1e3}s`,e.style.transitionTimingFunction="cubic-bezier(0.4, 0.0, 0.2, 1)"},show(){n()},end(){c&&(e.style.opacity=r.second.opacity),u&&(e.style.transform=`scale(${r.second.scale/100})`)},hide(){i()},cleanup(){c&&(e.style.opacity=o),u&&(e.style.transform=s),u&&(e.style.transformOrigin=a),e.style.transitionProperty=null,e.style.transitionDuration=null,e.style.transitionTimingFunction=null}};v(e,d)}function h(e,t,n,i,r,o){const s=e.__x_original_classes||[],a={start(){e.classList.add(...n)},during(){e.classList.add(...t)},show(){r()},end(){e.classList.remove(...n.filter(e=>!s.includes(e))),e.classList.add(...i)},hide(){o()},cleanup(){e.classList.remove(...t.filter(e=>!s.includes(e))),e.classList.remove(...i.filter(e=>!s.includes(e)))}};v(e,a)}function v(e,t){t.start(),t.during(),requestAnimationFrame(()=>{let n=1e3*Number(getComputedStyle(e).transitionDuration.replace(/,.*/,"").replace("s",""));t.show(),requestAnimationFrame(()=>{t.end(),setTimeout(()=>{t.hide(),e.isConnected&&t.cleanup()},n)})})}function g(e){return!isNaN(e)}function y(e,t,i,r,o){"template"!==t.tagName.toLowerCase()&&console.warn("Alpine: [x-for] directive should only be added to