├── README.md ├── app.html ├── app.js ├── css ├── loading.css └── main.css ├── fonts ├── Electrolize.woff └── Electrolize.woff2 ├── img ├── bg1.jpg ├── loader-light.png └── screen.jpg └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # Frosted Panel   [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Frosted%20Glass%2FPanel%20Effect%2C%20Cross%20Browser%2C%20%28Vainlla%20JS%20%26%20CSS%29&url=https://github.com/cryptodescriptor/frosted-panel&hashtags=css,javascript,design,frostedglass,frostedpanel,effect) 2 | 3 | Frosted Panel is a cross-browser compatible library written in pure Javascript to achieve a responsive "Frosted Glass" effect. 4 | 5 | 6 | 7 | If you are trying to get the same effect [documented here](https://webdesign.tutsplus.com/tutorials/how-to-create-a-frosted-glass-effect-in-css--cms-32535), but need it to work on as many browsers as possible, even if they do not support `filter: blur()`, then Frosted Panel is the library for you. 8 | 9 | Check out the [live demo](https://cryptodescriptor.github.io/frosted-panel) so you can see a demonstration of what it looks like. Try re-sizing the browser to see the panels responsiveness. 10 | 11 | Browsers tested thus far: 12 | 13 | - Chrome 14 | - Firefox 15 | - Firefox Focus 16 | - Edge 17 | - Internet Explorer 18 | - Safari 19 | - Opera 20 | 21 | Although only a few browsers are listed above, it should work on the majority of them. The goal of this library is to be as compatible with as many browsers as possible. 22 | 23 | 24 | ## Getting Started 25 | 26 | To get started, you will need to fork this repository or [download as zip](https://github.com/cryptodescriptor/frosted-panel/archive/master.zip). 27 | 28 | 29 | ## Configuration 30 | 31 | Firstly, you will need to select the background-image you want to use. Frosted Panel now scales the image using `background-size: cover` as of BETA_v1.1, therefore the image size is no longer as relevant, we just recommend sizing to be at least `1920x1080` for optimal quality. You may wish to go even larger if you want it to look crisp on 4k monitors, but it will scale nonetheless. 32 | 33 | Now that you know what image you will be using, you must specify the path inside `app.html` on the attribute `xlink:href` of the `image` element. Please also change the `width` and `height` attributes to match the dimensions of your image. If you are using an image of `2880x1620` pixels, the `width` should have a value of `2880` and the `height` should have a value of `1620`. 34 | 35 | 36 | 37 | 38 | ## Attributes 39 | 40 | Now you have your background-image all set up, you need to configure the panel size, its margins, and optionally add some breakpoints to allow you to change the sizing on different viewports. 41 | 42 | For simplicity, Frosted Panel is configured using html attributes only. 43 | 44 | 45 | ## Width & Height 46 | 47 | To set the `width` and `height` of the panel, you need to set the `panel-dimensions` attribute on the div with the class `frosted-panel`. 48 | 49 |
50 | 51 | The `width` is the first value, and the `height` is the second value, **the two values should be delimited by a single space**. 52 | 53 | Values can be one of three types: `px`, `%`, `auto`: 54 | 55 | 56 | |Type| Description | 57 | |--|--| 58 | | px | Fixed size in pixels | 59 | | % | Percentage of viewport width or height | 60 | | auto | Auto width or height based on content size | 61 | 62 | 63 | ## Content & Content Margin 64 | 65 | All the content that you wish to be inside the panel, must be nested within the `content` div. If you want to control the margin around the content (the space between the content and the edge of the panel), then you must do so by setting `content-margin` attribute on the div, with a pixel value. If this attribute is missing or empty, then the margin will default to 0. 66 | 67 |
68 | 69 | 70 | ## Appending Content 71 | 72 | **As of BETA_v2.1 this should no longer be necessary!** 73 | 74 | If you plan to append content to the panel programmatically, after you do so, you will need to call: 75 | 76 | frostedPanel.pan_and_zoom() 77 | 78 | To re-calculate the `width` and `height` if they are set to `auto`. 79 | 80 | ## Panel Margin 81 | 82 | On the `body` tag, you can add an optional attribute `space-top-bot`, which works by setting `min-height` of the page to a value of `panel_height + (space-top-bot*2)`, every time the screen is resized. This allows us to emulate a top and bottom `margin` for our panel. Currently there is no way to set a left and right margin, I didn't add this feature because generally you will be using percentage width's for your panel anyway. 83 | 84 | If you do use fixed width's, make sure the panel `width` is small enough to fit on the viewport's of your website visitors, otherwise the panel will overflow the body. 85 | 86 | 87 | 88 | The above code will add 50px top margin and 50px bottom margin. 89 | 90 | 91 | ## Blur Amount (stdDeviation) 92 | To adjust the "blurryness" of the panel, you will need to change the value of `stdDeviation` attribute (nested inside the `filter` element of the `svg`). The higher the number, the more blurry your panel will be. 93 | 94 | 95 | 96 | 97 | ## Breakpoints 98 | 99 | You can configure the panel to be different sizes at different viewport width's using the `breakpoints` attribute in conjunction with `breakpoint-type` attribute. 100 | 101 | You should be familiar with CSS media queries that make use of `min-width` and `max-width` conditions to apply a set of styles at certain viewport width's (breakpoints). 102 | 103 | Frosted Panel gives you freedom of choice by allowing you to provide `breakpoint-type` attribute, with a value of either `min-width` (mobile first), or `max-width`. The performance impact will be exactly the same for both, just use the one you feel the most comfortable with. 104 | 105 | Example: 106 | 107 |
108 | 109 | Great! Now you have selected which media query type you will be using, you can start to tell Frosted Panel how big you want it to be at different viewport width's. 110 | 111 | You do this by passing a list of breakpoints to the `breakpoints` attribute, each delimited by a single comma `,`. 112 | 113 | Each breakpoint must follow a specific format. The first value is the `width` in pixels that you want the breakpoint to occur at (must end with "`px`"), the second and third values are `width` and `height` values which determine the size of the panel when the breakpoint is triggered. The `width` and `height` value's each can be one of three format's described in the "Width & Height" section. 114 | 115 | Examples: 116 | 117 |
118 | 119 | The CSS equivalent of the above would be: 120 | 121 | .frosted-panel { 122 | width: auto; 123 | height: auto; 124 | } 125 | 126 | @media only screen and (min-width: 600px) { 127 | .frosted-panel { 128 | width: 70%; 129 | height: 300px; 130 | } 131 | } 132 | 133 | @media only screen and (min-width: 1200px) { 134 | .frosted-panel { 135 | width: 60%; 136 | height: 500px; 137 | } 138 | } 139 | 140 | When `breakpoint-type` is changed from `min-width` to `max-width`, the CSS equivalent would now be this: 141 | 142 | .frosted-panel { 143 | width: auto; 144 | height: auto; 145 | } 146 | 147 | @media only screen and (max-width: 1200px) { 148 | .frosted-panel { 149 | width: 60%; 150 | height: 500px; 151 | } 152 | } 153 | 154 | @media only screen and (max-width: 600px) { 155 | .frosted-panel { 156 | width: 70%; 157 | height: 300px; 158 | } 159 | } 160 | 161 | As you can see, the breakpoint attributes emulate standard CSS media queries. And are mandatory if you wish to use media queries to control the size of Frosted Panel. 162 | -------------------------------------------------------------------------------- /app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frosted Panel Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 | 39 |
40 | 80% 60% 41 | 80% 80% 42 | 60% 500px 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // endsWith polyfill IE11 2 | if (!String.prototype.endsWith) { 3 | String.prototype.endsWith = function(search, this_len) { 4 | if (this_len === undefined || this_len > this.length) { 5 | this_len = this.length; 6 | } 7 | return this.substring(this_len - search.length, this_len) === search; 8 | }; 9 | } 10 | 11 | /* Frosted Panel - sourced from https://github.com/cryptodescriptor/frosted-panel */ 12 | 13 | var frostedPanel = { 14 | e : { 15 | img : document.querySelector('image'), 16 | svg : document.querySelector('svg'), 17 | panel : document.querySelector('.frosted-panel'), 18 | content : document.querySelector('.content'), 19 | html : document.documentElement 20 | }, 21 | 22 | config : { 23 | 'breakpoints' : [] 24 | }, 25 | 26 | error : function(s) { 27 | return false; 28 | }, 29 | 30 | valid_num : function(str) { 31 | return (!isNaN(str) && str != "" && str != null); 32 | }, 33 | 34 | validate_wh : function(val) { 35 | var val = val.toLowerCase(); 36 | var valid_num = false; 37 | 38 | if (val === 'auto') { 39 | return true; 40 | } else if (!val.endsWith('px') && !val.endsWith('%')) { 41 | return false; 42 | } else if (val.endsWith('px')) { 43 | valid_num = this.valid_num(val.replace('px', '')); 44 | } else if (val.endsWith('%')) { 45 | valid_num = this.valid_num(val.replace('%', '')); 46 | } 47 | 48 | return valid_num; 49 | }, 50 | 51 | ignoring_breakpoint_err : function(breakpoint) { 52 | return this.error('Ignoring breakpoint: "' + breakpoint + '"'); 53 | }, 54 | 55 | invalid_breakpoint_err : function(breakpoint, val, i) { 56 | var joined = breakpoint.join(' '); 57 | this.error('Invalid value "' + val + '" at breakpoint: "' + joined + '", index: ' + i); 58 | return this.ignoring_breakpoint_err(joined); 59 | }, 60 | 61 | valid_breakpoint_arg_length : function(breakpoint) { 62 | if (breakpoint.length !== 3) { 63 | var joined = breakpoint.join(' '); 64 | this.error('Expected 3 values at breakpoint: "' + joined + '"'); 65 | return this.ignoring_breakpoint_err(joined); 66 | } 67 | return true; 68 | }, 69 | 70 | valid_breakpoint_property : function(breakpoint) { 71 | var b = breakpoint[0].toLowerCase(); 72 | // make sure breakpoint ends with px and is a number 73 | if (!b.endsWith('px') || !this.valid_num(b.replace('px', ''))) { 74 | return this.invalid_breakpoint_err(breakpoint, b, 0); 75 | } 76 | return true; 77 | }, 78 | 79 | valid_breakpoint_values : function(breakpoint) { 80 | // validate width and height values 81 | var val; 82 | 83 | for (var i = 1; i < 3; i++) { 84 | val = breakpoint[i]; 85 | 86 | if (!this.validate_wh(val)) { 87 | return this.invalid_breakpoint_err(breakpoint, val, i); 88 | } 89 | } 90 | return true; 91 | }, 92 | 93 | valid_breakpoint : function(breakpoint) { 94 | if ( 95 | !this.valid_breakpoint_arg_length(breakpoint) || 96 | !this.valid_breakpoint_property(breakpoint) || 97 | !this.valid_breakpoint_values(breakpoint) 98 | ) { 99 | return false; 100 | } 101 | return true; 102 | }, 103 | 104 | store_valid_breakpoints : function(breakpoints) { 105 | var breakpoint; 106 | 107 | for (var i = 0; i < breakpoints.length; i++) { 108 | // breakpoint, width, height 109 | breakpoint = breakpoints[i].trim().split(' '); 110 | 111 | // store breakpoint as a number 112 | if (this.valid_breakpoint(breakpoint)) { 113 | breakpoint[0] = parseInt(breakpoint[0]); 114 | this.config.breakpoints.push(breakpoint); 115 | } 116 | } 117 | }, 118 | 119 | load_breakpoints : function() { 120 | // check for breakpoint attr 121 | var panel_breakpoints = this.e.panel.getAttribute('breakpoints'); 122 | 123 | // if attribute missing, return 124 | if (!panel_breakpoints) return; 125 | 126 | // parse and store valid breakpoints 127 | var breakpoints = panel_breakpoints.split(','); 128 | this.store_valid_breakpoints(breakpoints); 129 | 130 | // sort: ascending min-width, descending max-width 131 | this.config.breakpoints = this.config.breakpoints.sort(function(a, b) { 132 | return (frostedPanel.config.brType === 'max-width') ? (b[0] - a[0]) : (a[0] - b[0]); 133 | }); 134 | }, 135 | 136 | valid_breakpoint_type : function(brType) { 137 | var brTypes = ['min-width', 'max-width']; 138 | 139 | if (brTypes.indexOf(brType.toLowerCase()) === -1) { 140 | return this.error('Invalid breakpoint-type attribute!'); 141 | } 142 | 143 | return true; 144 | }, 145 | 146 | load_attributes : function() { 147 | var c = this.config; 148 | 149 | c['imageWidth'] = this.e.img.getAttribute('width'); 150 | c['imageHeight'] = this.e.img.getAttribute('height'); 151 | c['spaceTopBot'] = parseInt(document.body.getAttribute('space-top-bot')) || 0; 152 | c['contentMargin'] = parseInt(this.e.content.getAttribute('content-margin')) || 0; 153 | 154 | var brType = this.e.panel.getAttribute('breakpoint-type') || 'min-width'; 155 | 156 | if (!this.valid_breakpoint_type(brType)) return false; 157 | 158 | c['brType'] = brType; 159 | 160 | return true; 161 | }, 162 | 163 | attribute_exists : function(attr) { 164 | return (!attr) ? this.error('Empty/Missing required attr "' + attr + '"!') : true; 165 | }, 166 | 167 | valid_attribute_length : function(attr, wh) { 168 | if (wh.length !== 2) { 169 | return this.error('Unexpected length "' + wh.length + '" for "' + attr + '" attr!'); 170 | } 171 | return true; 172 | }, 173 | 174 | valid_panel_wh_values : function(attr, wh) { 175 | for (var i = 0; i < wh.length; i++) { 176 | if (!this.validate_wh(wh[i])) { 177 | return this.error('Invalid value "' + wh[0] + '" for "' + attr + '" attr!'); 178 | } 179 | } 180 | return true; 181 | }, 182 | 183 | load_config : function() { 184 | var attr = 'panel-dimensions'; 185 | var panel_dimensions = this.e.panel.getAttribute(attr); 186 | 187 | if (!this.attribute_exists(panel_dimensions)) return false; 188 | 189 | var wh = panel_dimensions.split(' '); 190 | 191 | if (!this.valid_attribute_length(attr, wh) || !this.valid_panel_wh_values(attr, wh)) { 192 | return false; 193 | } 194 | 195 | this.config['width'] = wh[0]; 196 | this.config['height'] = wh[1]; 197 | 198 | // load the rest of the attributes 199 | if (!this.load_attributes()) return false; 200 | 201 | // load breakpoints 202 | this.load_breakpoints(); 203 | 204 | return true; 205 | }, 206 | 207 | is_suitable_breakpoint : function(breakpoint) { 208 | return window.matchMedia('('+this.config.brType+': '+breakpoint[0]+'px)').matches; 209 | }, 210 | 211 | find_suitable_breakpoint : function() { 212 | var breakpoint = null; 213 | 214 | for (var i = this.config.breakpoints.length-1; i >= 0; i--) { 215 | if (this.is_suitable_breakpoint(this.config.breakpoints[i])) { 216 | breakpoint = this.config.breakpoints[i]; 217 | break; 218 | } 219 | } 220 | 221 | return breakpoint; 222 | }, 223 | 224 | fetch_breakpoint : function() { 225 | // if breakpoints are empty 226 | if (this.config.breakpoints.length === 0) return null; 227 | 228 | // if we don't currently need a breakpoint 229 | if (!this.is_suitable_breakpoint(this.config.breakpoints[0])) { 230 | return null; 231 | } 232 | 233 | return this.find_suitable_breakpoint(); 234 | }, 235 | 236 | auto : { 237 | 'width' : null, 238 | 'height' : null 239 | }, 240 | 241 | toggle_auto : function(type) { 242 | if (this.auto[type] === false || this.auto[type] === null) { 243 | this.e.content.style[type] = 'auto'; 244 | this.e.panel.style[type] = 'auto'; 245 | this.auto[type] = true; 246 | } 247 | }, 248 | 249 | toggle_fixed : function(type, panelWidthOrHeightPx) { 250 | this.e.content.style[type] = panelWidthOrHeightPx - (this.config.contentMargin*2) + 'px'; 251 | this.e.panel.style[type] = panelWidthOrHeightPx + 'px'; 252 | this.auto[type] = false; 253 | }, 254 | 255 | set_percentage : function(viewportWidthOrHeight, val, type) { 256 | var px = (viewportWidthOrHeight/100)*val.replace('%', ''); 257 | this.toggle_fixed(type, px); 258 | return px; 259 | }, 260 | 261 | set_fixed : function(val, type) { 262 | var px = parseInt(val.replace('px', '')); 263 | this.toggle_fixed(type, px); 264 | return px; 265 | }, 266 | 267 | set_auto : function(type) { 268 | var m = (this.config.contentMargin*2); 269 | this.toggle_auto(type); 270 | return ((type === 'width') ? (this.e.content.clientWidth+m) : (this.e.content.clientHeight+m)); 271 | }, 272 | 273 | set_pixel_val : function(val, viewportWidthOrHeight, type) { 274 | if (val.endsWith('%')) { 275 | return this.set_percentage(viewportWidthOrHeight, val, type); 276 | } else if (val.endsWith('px')) { 277 | return this.set_fixed(val, type); 278 | } else if (val.toLowerCase() === 'auto') { 279 | return this.set_auto(type); 280 | } 281 | }, 282 | 283 | calc_cover_size : function() { 284 | // calculate the size of the scaled bg image 285 | var width = this.config.imageWidth; 286 | var height = this.config.imageHeight; 287 | 288 | var object = document.body; 289 | 290 | // Step 1 - Get the ratio of the div + the image 291 | var imageRatio = width/height; 292 | var coverRatio = object.offsetWidth/object.offsetHeight; 293 | 294 | // Step 2 - Work out which ratio is greater 295 | if (imageRatio >= coverRatio) { 296 | // The Height is our constant 297 | var coverHeight = object.offsetHeight; 298 | var scale = coverHeight / height; 299 | var coverWidth = width * scale; 300 | } else { 301 | // The Width is our constant 302 | var coverWidth = object.offsetWidth; 303 | var scale = coverWidth / width; 304 | var coverHeight = height * scale; 305 | } 306 | 307 | return [coverWidth, coverHeight, scale]; 308 | }, 309 | 310 | set_panel_width_and_height : function(viewportWidth, viewportHeight) { 311 | // See if we hit a breakpoint 312 | var breakpoint = this.fetch_breakpoint(); 313 | 314 | if (breakpoint === null) { 315 | var w = this.config.width; 316 | var h = this.config.height; 317 | } else { 318 | var w = breakpoint[1]; 319 | var h = breakpoint[2]; 320 | } 321 | 322 | // Convert to pixels and set width + height 323 | var w = this.set_pixel_val(w, viewportWidth, 'width'); 324 | var h = this.set_pixel_val(h, viewportHeight, 'height'); 325 | 326 | // Return w,h so we can calc pan + zoom values 327 | return [w, h]; 328 | }, 329 | 330 | previous_viewport_w : null, 331 | previous_viewport_h : null, 332 | 333 | viewport_size_not_changed : function(viewportWidth, viewportHeight) { 334 | return ( 335 | this.previous_viewport_w === viewportWidth && 336 | this.previous_viewport_h === viewportHeight 337 | ); 338 | }, 339 | 340 | get_device_width_and_height : function() { 341 | return [ 342 | (window.innerWidth) ? window.innerWidth : document.documentElement.clientWidth, 343 | (window.innerHeight) ? window.innerHeight : document.documentElement.clientHeight 344 | ]; 345 | }, 346 | 347 | prepare_pan_and_zoom : function() { 348 | // Get viewport width and height 349 | var viewPortWH = this.get_device_width_and_height(); 350 | var viewportWidth = viewPortWH[0]; 351 | var viewportHeight = viewPortWH[1]; 352 | 353 | // Don't need to do anything if viewport size didn't change 354 | if (this.viewport_size_not_changed(viewportWidth, viewportHeight)) { 355 | return null; 356 | } 357 | 358 | this.previous_viewport_w = viewportWidth; 359 | this.previous_viewport_h = viewportWidth; 360 | 361 | // Set Panel width and height 362 | var wh = this.set_panel_width_and_height(viewportWidth, viewportHeight); 363 | var panelW = wh[0]; 364 | var panelH = wh[1]; 365 | 366 | // Set html minHeight for padding effect 367 | this.e.html.style.minHeight = (panelH + (this.config.spaceTopBot*2)) + 'px'; 368 | 369 | // Get size of scaled background image 370 | var width_height_scale = this.calc_cover_size(); 371 | var imageWidth = width_height_scale[0]; 372 | var imageHeight = width_height_scale[1]; 373 | 374 | // Make svg behave like a centered background image 375 | var cropX = (imageWidth-viewportWidth)/2; 376 | var cropY = (imageHeight-viewportHeight)/2; 377 | 378 | // Calculate how much we need to pan 379 | var panW = -(viewportWidth-panelW)/2 - cropX; 380 | var panH = -(viewportHeight-panelH)/2 - cropY; 381 | 382 | var scale = width_height_scale[2]; 383 | 384 | return [panW, panH, scale]; 385 | }, 386 | 387 | pan_and_zoom : function() { 388 | var panW_panH_scale = this.prepare_pan_and_zoom(); 389 | if (panW_panH_scale === null) return; 390 | var panW = panW_panH_scale[0]; 391 | var panH = panW_panH_scale[1]; 392 | var scale = panW_panH_scale[2]; 393 | this.e.img.setAttribute('transform', 'translate('+panW+' '+panH+') scale('+scale+')'); 394 | }, 395 | 396 | started : false, 397 | bg_img : null, 398 | 399 | ready : function(callback) { 400 | // Check the background image is loaded before starting frostedPanel 401 | var src = this.e.img.getAttribute('xlink:href'); 402 | 403 | this.bg_img = src; 404 | 405 | var img = new Image(); 406 | 407 | img.onload = function() { 408 | if (!this.started) { 409 | this.started = true; 410 | callback(); 411 | } 412 | } 413 | 414 | img.src = src; 415 | }, 416 | 417 | start_panel : function() { 418 | // Start Resize Listener 419 | window.addEventListener("resize", function() { 420 | frostedPanel.pan_and_zoom(); 421 | }); 422 | 423 | // Do initial pan and zoom 424 | this.pan_and_zoom(); 425 | 426 | // Hide loading and display panel 427 | window.parent.postMessage('hideLoad', '*'); 428 | this.e.panel.style.visibility = 'visible'; 429 | }, 430 | 431 | init : function() { 432 | var bg = this.bg_img; 433 | 434 | // Set background image 435 | document.body.style.backgroundImage = 'url(' + bg + ')'; 436 | 437 | // Set content margin 438 | this.e.content.style.margin = frostedPanel.config.contentMargin + 'px'; 439 | 440 | window.onload = function() { 441 | frostedPanel.start_panel(); 442 | }; 443 | } 444 | } 445 | 446 | var loaded = frostedPanel.load_config(); 447 | 448 | if (loaded) { 449 | frostedPanel.ready(function() { 450 | frostedPanel.init(); 451 | }); 452 | } else { 453 | frostedPanel.error('frostedPanel aborted!'); 454 | } -------------------------------------------------------------------------------- /css/loading.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100%; 3 | height: 100%; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | .flex-center-col { 9 | display: -webkit-box; 10 | display: -ms-flexbox; 11 | display: flex; 12 | -webkit-box-align: center; 13 | -ms-flex-align: center; 14 | align-items: center; 15 | -webkit-box-pack: center; 16 | -ms-flex-pack: center; 17 | justify-content: center; 18 | } 19 | 20 | #loading { 21 | z-index: 9999; 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | width: 100%; 26 | height: 100%; 27 | background: rgb(249, 249, 250); 28 | -webkit-box-flex: 0; 29 | -ms-flex: 0 0 auto; 30 | flex: 0 0 auto; 31 | } 32 | 33 | @-webkit-keyframes delayedSpinner { 34 | 0% { 35 | -webkit-transform:rotate(0deg); 36 | transform:rotate(0deg); 37 | } 38 | to { 39 | -webkit-transform:rotate(1turn); 40 | transform:rotate(1turn); 41 | } 42 | } 43 | 44 | @keyframes delayedSpinner { 45 | 0% { 46 | -webkit-transform:rotate(0deg); 47 | transform:rotate(0deg); 48 | } 49 | to { 50 | -webkit-transform:rotate(1turn); 51 | transform:rotate(1turn); 52 | } 53 | } 54 | 55 | .page-loading__icon { 56 | overflow : hidden; 57 | width : 30px; 58 | height : 30px; 59 | background : url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAMAAABjROYVAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAwBQTFRFAAAAZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm+zfHiAAAAQB0Uk5TAC5beqi42Pb3+Pn6+/zev7CAYDAPSoaz7/Dx8vP09f3+/8CQUBAdZq/q6+zt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') no-repeat; 60 | background-size : contain; 61 | -webkit-animation : delayedSpinner .61s linear infinite; 62 | animation : delayedSpinner .61s linear infinite; 63 | } 64 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding: 0; 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background-position: center; 12 | background-repeat: no-repeat; 13 | background-attachment: local; 14 | background-size: cover; 15 | display: -webkit-box; 16 | display: -ms-flexbox; 17 | display: flex; 18 | -webkit-box-align: center; 19 | -ms-flex-align: center; 20 | align-items: center; 21 | -webkit-box-pack: center; 22 | -ms-flex-pack: center; 23 | justify-content: center; 24 | } 25 | 26 | .frosted-panel { 27 | -webkit-box-shadow: 0 0 1rem 0 rgba(0, 0, 0, .2); 28 | box-shadow: 0 0 1rem 0 rgba(0, 0, 0, .2); 29 | position: relative; 30 | border-radius: 5px; 31 | overflow: hidden; 32 | visibility: hidden; 33 | width: 0; 34 | height: 0; 35 | -webkit-box-flex: 0; 36 | -ms-flex: 0 0 auto; 37 | flex: 0 0 auto; 38 | } 39 | 40 | svg { 41 | overflow: hidden; 42 | height: 100%; 43 | width: 100%; 44 | position: absolute; 45 | top: 0; 46 | right: 0; 47 | z-index: -1; 48 | } 49 | 50 | .content { 51 | z-index: 1; 52 | } 53 | 54 | /* EVERYTHING BELOW THIS POINT IS FOR DEMO PURPOSES ONLY */ 55 | 56 | .flex-container { 57 | width: 100%; 58 | height: 100%; 59 | display: -webkit-box; 60 | display: -ms-flexbox; 61 | display: flex; 62 | -webkit-box-align: center; 63 | -ms-flex-align: center; 64 | align-items: center; 65 | -webkit-box-pack: center; 66 | -ms-flex-pack: center; 67 | justify-content: center; 68 | } 69 | 70 | .wh { 71 | font-size: 30px; 72 | font-family: 'Electrolize', sans-serif; 73 | white-space: nowrap; 74 | } 75 | 76 | .wh:nth-child(1) { 77 | display: inline; 78 | } 79 | 80 | .wh:nth-child(2), 81 | .wh:nth-child(3) { 82 | display: none; 83 | } 84 | 85 | @media only screen and (min-width: 600px) { 86 | .wh { 87 | font-size: 40px; 88 | } 89 | 90 | .wh:nth-child(1), 91 | .wh:nth-child(3) { 92 | display: none; 93 | } 94 | 95 | .wh:nth-child(2) { 96 | display: inline; 97 | } 98 | } 99 | 100 | @media only screen and (min-width: 1200px) { 101 | .wh { 102 | font-size: 40px; 103 | } 104 | 105 | .wh:nth-child(1), 106 | .wh:nth-child(2) { 107 | display: none; 108 | } 109 | 110 | .wh:nth-child(3) { 111 | display: inline; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /fonts/Electrolize.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cryptodescriptor/frosted-panel/2fa3d92bd799eb24fe17a741f15ba7a97d765191/fonts/Electrolize.woff -------------------------------------------------------------------------------- /fonts/Electrolize.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cryptodescriptor/frosted-panel/2fa3d92bd799eb24fe17a741f15ba7a97d765191/fonts/Electrolize.woff2 -------------------------------------------------------------------------------- /img/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cryptodescriptor/frosted-panel/2fa3d92bd799eb24fe17a741f15ba7a97d765191/img/bg1.jpg -------------------------------------------------------------------------------- /img/loader-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cryptodescriptor/frosted-panel/2fa3d92bd799eb24fe17a741f15ba7a97d765191/img/loader-light.png -------------------------------------------------------------------------------- /img/screen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cryptodescriptor/frosted-panel/2fa3d92bd799eb24fe17a741f15ba7a97d765191/img/screen.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frosted Panel Demo 5 | 6 | 7 | 8 | 9 | 21 | 22 | 23 |
24 |
25 |
26 | 27 | 36 | 37 | 38 | 39 | 40 | --------------------------------------------------------------------------------