Stefan Baumgarnter's blog 2018-02-20T14:38:32+00:00 http://www.fettblog.eu Stefan Baumgartner sbaumg@gmail.com Saving and scraping a website with Puppeteer 2018-02-20T00:00:00+00:00 http://www.fettblog.eu/scraping-with-puppeteer <p>For some of my performance audits I need an exact copy of the webpage as it is served by my clients infrastructure. In some cases, it can be hard to get to the actual artefact. So it’s easier to fetch it from the web.</p> Cutting the mustard - 2018 edition 2018-02-13T00:00:00+00:00 http://www.fettblog.eu/cutting-the-mustard-2018 <p>The other day I was holding a workshop on performance optimisation for single page applications. For this workshop I needed an example that I could optimise step by step. I decided not to use a framework, as I didn’t know the experiences and background of my attendees. Also, I didn’t want to draw attention to framework details, rather focus on concepts that build on the platform and that are universally applicable to SPAs.</p> Wordpress as CMS for your JAMStack sites 2018-01-08T00:00:00+00:00 http://www.fettblog.eu/wordpress-and-jamstack-sites <p>The almighty JAMStack brings you fast and secure static websites, and with things like <a href="https://storyblok.com">headless content management systems</a> they become even easy to edit! However, every once in a while you will find yourself in front of a Wordpress blog that has way too many articles (and way too many authors that fear change!) to be reasonably transferred. But Wordpress can be headless, too. In fact, Wordpress’ own hosting service uses its core only via API, the editing interface comes with the shiny new <a href="https://developer.wordpress.com/calypso/">Calypso</a>.</p> My most favourite podcast episodes in 2017 2017-12-31T00:00:00+00:00 http://www.fettblog.eu/top-podcast-episodes-2017 <p>After my <a href="/top-books-to-read-2017/">most favourite tech books</a> and my <a href="/top-talks-to-watch-2017/">most favourite tech talks</a> I want to conclude my yearly review with some of podcast episodes I really enjoyed! I listen to one or two hours of podcasts a day. Most of the podcasts I subscribed to are about growing up in the 80s and loving things like the Nintendo Entertainment System or Indiana Jones. But I also listen to a couple of tech podcasts occasionally. And there I found some true gems. Let’s go!</p> My most favourite talks in 2017 2017-12-30T00:00:00+00:00 http://www.fettblog.eu/top-talks-to-watch-2017 <p>So it IS finally a tradition. After <em><a href="/top-talks-to-watch-2015/">my most favourite talks of 2015</a></em>, and <em><a href="/top-talks-to-watch-2016/">the top talks to watch in 2016 – the conference videos strike back!</a></em>, we complete the trilogy with <em>my most favourite talks of 2017 – Return of the Bingewatch</em>:</p> My most favourite books in 2017 2017-12-28T00:00:00+00:00 http://www.fettblog.eu/top-books-to-read-2017 <p>I started taking reading seriously again in 2017. I had highs where I read about 10 books a month (phew), and also lows where I haven’t touched a page for a couple of months. Now with having both a toddler and a Kindle<sup>*</sup>, I gladly spend night again guarding the crib while reading both fiction and non-fiction. And oh, did I read a lot of non-fiction this year. There were some amazing books, and I want to share with you the ones that I found the most compelling.</p> The Best Request Is No Request, Revisited 2017-11-29T00:00:00+00:00 http://www.fettblog.eu/the-best-request <p>I had the incredible privilege to work with the fine folks at <a href="https://www.alistapart.com">A List Apart</a> on a text concerning resource bundling in the age of HTTP/2. Using the powers of the new protocol is something that keeps my head busy <a href="https://www.youtube.com/watch?v=98z0XjYWX0o">for quite some time now</a>, and I had to write down my findings. Aaron and Jeremy from ALA were kind enough to accept this piece and work with me on it. It’s been a very thorough process, but it strengthens my belief that an editor’s work can’t be valued enough. Thanks for all your help in getting this one out!</p> Not so hidden figures - Organizing ScriptConf 2017-08-09T00:00:00+00:00 http://www.fettblog.eu/not-so-hidden-figures <p>I met Laura from <a href="http://foundation.travis-ci.org">Travis Foundation</a> a couple of years ago at a conference in Austria, and we had some good time and nice chats together. It’s not easy to keep always in touch with everyone you meet a conference, but thankfully Laura and I managed to cross paths mutliple times in the last few years. I was also delighted to work with her on bringing diversity tickets to Script’17. After attending Script’17, she asked me to write about my experiences in organizing and getting such a wonderful line-up on stage. Laura was super patient, as it took me about six months to finally get the piece done. But here it is, finally! Check out <a href="http://foundation.travis-ci.org/2017/08/09/not-so-hidden-figures/">Not so hidden figures - organizing ScriptConf</a> over at Travis Foundation!</p> My podcast journey to ScriptCast 2017-07-11T00:00:00+00:00 http://www.fettblog.eu/my-new-podcast-scriptcast <p>I love podcasting. I started back in 2009 and hosted a literature podcast for roughly two years. That’s where I made my first steps in producing non-written content and had lots of fun! I fiddled around doing interviews, chats, specials and audio dramas. Trying to read certain passages of books and added some sound effects to make those passages more interesting. Best thing was buying a semi-professional equipment and record public readings from authors directly from the mixer.</p> Grid layout, grid layout everywhere! 2017-06-27T00:00:00+00:00 http://www.fettblog.eu/grid-concepts <p>One of the great things about Microsoft Edge is that as a developer, you always know what to expect from an upcoming version. Communication is key! The <a href="https://developer.microsoft.com/en-us/microsoft-edge/platform/status/">platform status page</a> gives you a by feature list of the current development status, and the <a href="https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6514853-update-css-grid">Edge user voice</a> allows you to actively influence the developers’ backlog!</p> <p>I used this privilege to cast my three votes to the “Update CSS Grid” feature request. <a href="https://channel9.msdn.com/Events/Build/2012/3-114">IE 10 was one of the first browsers to implement the CSS Grid specification</a>. This specification comes from a time where Microsoft pushed the web platform hard to be a fully competitive platform for app development. And for that, it needed a proper layout mechanism. Hello, grid layouts! The original spec was pretty early and has been improved over the years. The new specification has reached candidate recommendation and has been since implement in … well, all major desktop browsers! The only one missing was Edge, until last week, when I got a notification from the Microsoft Edge Developer User Voice:</p> <p><img src="/wp-content/uploads/grid/grid0.png" alt="Grid landed in Edge preview" /></p> #scriptconf and #devone 2017-06-03T00:00:00+00:00 http://www.fettblog.eu/scriptconf-and-devone <p>About two years ago, <a href="https://twitter.com/sebgie">Sebastian</a> and I had our first chats about having a JavaScript conference in our town of Linz. And this January, we made our vision reality and launched <a href="https://scriptconf.org">ScriptConf</a>, a conference about JavaScript. Giving it the (quite bold) tagline “The conference this city needs”, we wanted to make JavaScript more popular in Linz, while having a great time with an amazing community.</p> Object streams in Node.js 2017-04-19T00:00:00+00:00 http://www.fettblog.eu/object-streams-in-node-js <p>I’m really good friends with the people at Rising Stack in Budapest. I had lots of fun when they invited me to JSConf Budapest back in May, and enjoy every time they show up here in Linz! I’m also looking forward to see them again when Peter will talk at the upcoming <a href="https://devone.at">Devone</a> conference. So it’s clear that I was more than eager to produce a guest post for their <a href="https://community.risingstack.com">Rising Stack community</a>. Even though I was so eager, it took me roughly 3 months to produce an article based on my recent book “Front-End tooling”. Nevertheless, I thought that my guide to object streams turned out pretty good! I hope <a href="https://community.risingstack.com/the-definitive-guide-to-object-streams-in-node-js/">you enjoy it</a>!</p> Guest post in "Human and the machine": Get the gist 2017-03-20T00:00:00+00:00 http://www.fettblog.eu/human-and-the-machine <p>Back in the day I was an avid reader of <a href="https://the-pastry-box-project.net/">The pastry box</a>. You can imagine how super happy I was when Alex, the project’s creator asked me to contribute to his new project <a href="https://superyesmore.com/publication/the-human-in-the-machine-a4064599cde2cb3397239e8d72219f48">Human and the machine</a>. It’s all about productivity, in all shapes and forms imaginable. I wrote about “<a href="https://superyesmore.com/get-the-gist-8ff35dc28f30640e2d5fd54bcb7ff083">getting the gist</a>”, and how I manage to stay up to date without having to become an expert myself. Contributing was a wonderful experience, thanks again to Alex for having me and for this great opportunity!</p> I was a guest on the Perfbytes podcast 2017-02-22T00:00:00+00:00 http://www.fettblog.eu/guest-on-perfbytes <p>The <a href="http://perfbytes.com">Perfbytes</a> crew was live at this year’s Perform conference, interviewing crew, attendees and speakers. I had the chance to have a few minutes with them talking about the <a href="https://www.dynatrace.com/capabilities/digital-experience-monitoring/visually-complete/">Visually Complete</a> metric and what it means for Real User Monitoring. Being a podcaster since 2009, it was funny for me doing an interview live from the other side of the microphone. But an experience I fully enjoyed, also mostly due to Mark’s excellent interviewing skills. I should do more live podcasts! If you like, give it a listen at <a href="https://www.spreaker.com/user/pureperformance/dynatrace-perform-2017-wednesday-highlig">Spreaker</a>. It’s a long show, my part starts roughly at 37 minutes. And keep in mind that I was super jetlagged. Thanks Mark and Brian for having me!</p> This was Script'17 2017-02-16T00:00:00+00:00 http://www.fettblog.eu/this-was-script-17 <p>Sebastian and I gathered together a <a href="https://scriptconf.org/blog/script17-roundup">list of links and lots of coverage</a> to our conference we held in January. Lots of great blogs, podcasts, images and tweets, including a few words from our side. We had so much fun organising the conference and a wonderful time being there. Thanks to everybody for coming.</p> Real user speed index and visually complete 2017-02-07T00:00:00+00:00 http://www.fettblog.eu/visually-complete <p>This is a somewhat personal post today. I’m currently in Las Vegas (which would be a story on its own) to speak and attend our company’s <a href="https://www.dynatrace.com/perform">Perform conference</a>. Why me, as a mere web developer with no customer contact? First, I do like to bring the developer’s view into the whole mix. Second, I want to see the birth of a new product feature.</p> Plumbin' Pipelines with Gulp.js (Workshop) 2017-01-15T00:00:00+00:00 http://www.fettblog.eu/plumbin-pipelines-with-gulp-js <p>Now that <a href="https://manning.com/baumgartner">my book</a> is out, I dug up an old workshop I held almost one and a half years ago in Belgium at Devoxx. Devoxx is a special type of conference, unlike any other conference I’ve ever seen. And speaking at a cinema definitely is one of the things that get stuck with you. Anyways, here’s the 2 hour course (the rest of the show is a little off topic, intentionally), which is about 75% of the Gulp chapters in live coding. Enjoy:</p> My most favorite talks in 2016 2016-12-31T00:00:00+00:00 http://www.fettblog.eu/top-talks-to-watch-2016 <p>Let’s make this a tradition! I love to watch conference talks. Be it live or on tape. And just like <a href="/top-talks-to-watch-2015/">last year</a> I try to collect the talks that I loved most. I know, since I started organising <a href="https://scriptconf.org">Script</a> I began seeing conference talks differently. However, those are the ones I put my attendee-hat on. And let’s be honest: A good organiser needs to do that! So enjoy my list of talks I enjoyed most in 2016.</p> Script'17 JavaScript conference 2016-10-26T00:00:00+00:00 http://www.fettblog.eu/script-17 <p>After years of attending conferences and gathering lots of impressions from around the world, my buddy Sebastian and I decided to do our own thing. We call it <a href="https://scriptconf.org">Script’17</a>, and aim to provide an exciting, caring and inspiring JavaScript event for everybody. If you’re interested, go ahead and check it out!</p> PortoTechHub 2016 2016-10-09T00:00:00+00:00 http://www.fettblog.eu/porto-tech-hub <p>I rarely talk about attending or speaking at conferences, but sometimes you just have to point out some extraordinary events.</p> Using a Static Site Generator at Scale 2016-08-08T00:00:00+00:00 http://www.fettblog.eu/smashing-magazine-static-site-generators <p>I’m so incredibly happy to have my first article published on <a href="https://www.smashingmagazine.com/2016/08/using-a-static-site-generator-at-scale-lessons-learned/">Smashing Magazine</a>. It’s a 6000 word long story about how we managed to bring roughly 2000 pages on a technology stack made for hackers. It took me roughly four years to find a story that I’m eager enough to pursue, but in the end I think it totally paid off. Working with Smashing Magazine, especially Vitaly and Iris, was a complete joy! Thank you again for the professional and friendly work experience!</p> Video and Slides to: Speed Index, Explained! 2016-02-07T00:00:00+00:00 http://www.fettblog.eu/speed-index-explained <p>I gave a talk on the beloved “Speed Index” metric at this year’s Topconf in Tallinn. I managed to put the video on Youtube, for all of you to enjoy:</p> Topconf Linz 2016 is a wrap 2016-02-05T00:00:00+00:00 http://www.fettblog.eu/topconf-at <p>So, Linz has a software conference. One that’s supposed to stay. I think it’s about time. Linz has a wonderful IT community, with lots of great companies and so many schools and universities specialised for software development. Except for the short-lived Kod.io movement (spanning <a href="/blog/2014/03/02/kodio/">Kod.io</a>, Codefront and Railsgirls in 2014), as well as some Linuxwochen now and then, I hardly can remember any full-fledged Software conference. So hey, we finally got one!</p> Tales from the Browser Wars: Mozilla Stomps IE 2016-01-28T00:00:00+00:00 http://www.fettblog.eu/mozilla-stomps-ie <p>Recently I did some “The History of the web” talks for various institutions. While preparing the story to tell, I remembered one specific tale from 1997. It was a tale from the first browser wars, when Microsoft battled Netscape for world browser domination.</p> You can extend CoffeeScript classes with ES6 classes 2016-01-27T00:00:00+00:00 http://www.fettblog.eu/extend-coffeescript-with-es6-classes <p><strong>TL;DR</strong>: If you want to extend from CoffeeScript written classes, you can use the ES6 class syntax to do so.</p> The best thing about NPM scripts 2016-01-25T00:00:00+00:00 http://www.fettblog.eu/npm-scripts <p><strong>TL;DR:</strong> NPM Scripts render global installations of NPM command line tools useless.</p> Gulp and Promises 2015-12-21T00:00:00+00:00 http://www.fettblog.eu/gulp-promises <p>The Gulp task system does not only work with streams alone, but also with other asynchronous patterns. One of those are well known Promises! Find out how we can use two Promise-based tools to create a thorough file sync between two folders.</p> My most favorite talks in 2015 2015-12-12T00:00:00+00:00 http://www.fettblog.eu/top-talks-to-watch-2015 <p>I enjoy being at conferences, however I can’t be on all of them. Which is sad, because judging from all the videos one misses a lot! Turns out, my most favorite talks were all from conferences I haven’t been to. The organizers however are kind enough to provide amazing videos for us:</p> Gulp: Grab files from your CDN and add them to your build pipeline 2015-10-13T00:00:00+00:00 http://www.fettblog.eu/gulp-merge-cdn-files-into-your-pipeline <p>This one is a shorty, but that’s what it makes it so nice. Imagine that you have only one dependency in your project, which is some third party library your code builds upon, like jQuery. Instead of having the complete dependency management stack on your shoulders, you just want to use that single file.</p> Gulp 4: The new task execution system - gulp.parallel and gulp.series 2015-09-29T00:00:00+00:00 http://www.fettblog.eu/gulp-4-parallel-and-series <p>One of the major changes in Gulp 4 is the new task execution system. In this article, I want to show you what’s new and how you can migrate the best.</p> Stay focused with Webmonitoring (ruxit.com) 2015-09-14T00:00:00+00:00 http://www.fettblog.eu/stay-focused-with-web-monitoring <p>Another article I wrote for the blog over at my employer. When we switched our login from a purely JavaScript based to a progressively enhanced one, we ended up in some deployment mistakes. This was how our tool recognized those errors and what it meant for us. Read it over at <a href="https://blog.ruxit.com/stay-focused-with-web-monitoring/">Ruxit</a></p> Node.js 4.0.0 and Gulp first aid 2015-09-10T00:00:00+00:00 http://www.fettblog.eu/gulp-and-node4-first-aid <p><a href="https://nodejs.org/en/blog/release/v4.0.0/">Node.js 4.0.0</a> just got released! The jump from 0.12.x to 4.0 is a huge one, especially since it incorporates lots of changes that happened over at the IO.js project. So far, I haven’t experienced a lot of issues with it, and it quickly became the one version that I use as default on my system. However, there are some hickups here and there. Here I try to collect some issues with Gulp.js and first aid solutions. There are few and they might be out of date quickly.</p> Gulp 4: Incremental builds with gulp.lastRun 2015-09-09T00:00:00+00:00 http://www.fettblog.eu/gulp-4-incremental-builds <p>Incremental builds are a good way of speeding up your build iterations. Instead of building everything again with each and every iteration, you just process the files that have changed.</p> Gulp 4: Passthrough source streams 2015-09-07T00:00:00+00:00 http://www.fettblog.eu/gulp-4-passthrough <p>Another nice addition to <code class="highlighter-rouge">vinyl-fs</code> that will end up in Gulp 4 is the possibility of having “passthrough” source streams. This basically allows <code class="highlighter-rouge">gulp.src</code> to be writable. So what does this mean for you?</p> JavaScript 101: Arrays 2015-09-04T00:00:00+00:00 http://www.fettblog.eu/javascript-101-arrays <p><em>This was the first contribution I’ve ever made at <a href="https://github.com/jquery/learn.jquery.com/pull/88">GitHub</a>, belonging to the original learn.jquery.com website. The original article is now offline, but saved here for the future.</em></p> Gulp 4: Built-in Sourcemaps 2015-09-03T00:00:00+00:00 http://www.fettblog.eu/gulp-4-sourcemaps <p>One really cool feature on the Gulp 4 roadmap is the inclusion of native sourcemaps. A commit roughly two weeks ago at the <a href="git://github.com/wearefractal/vinyl-fs">vinyl-fs</a> package makes this possible now. Instead of using the <code class="highlighter-rouge">gulp-sourcemaps</code> package directly, you can use a flag in <code class="highlighter-rouge">gulp.src</code>. Gulp takes care of the rest:</p> I'm writing a book: Front-End Tooling with Gulp, Bower and Yeoman 2015-08-26T00:00:00+00:00 http://www.fettblog.eu/gulp-book <p>Okay, lovely people, this is huge for me. I’m actually in the midst of writing a book. It’s called “<a href="https://www.manning.com/books/front-end-tooling-with-gulp-bower-and-yeoman/?a_aid=fettblog&amp;a_bid=238ac06a">Front-End Tooling with Gulp, Bower and Yeoman</a>”, and it’s – you guessed it – about front-end tooling with Gulp, Bower and Yeoman. Actually I’ve been writing on it for the last 9 months, and now it has reached the phase of going public for the first time. It’s in the so called “Early Access Program” from Manning (MEAP), where you can buy it before it goes to print, helping me pointing at all the mistakes I make. Or do some slap on the back because it’s so good.</p> Book Review: CSS Secrets by Lea Verou 2015-08-24T00:00:00+00:00 http://www.fettblog.eu/book-review-css-secrets <p>In 2012 I saw one of Lea Verou’s talks for the first time. And if you every had the opportunity of seeing her, you know that you are in for a treat. Her unique way of teaching all those nifty CSS tricks is not only entertaining and engaging, but also a huge motivation for your own work. Every time I returned home from one of those conferences, I tried recreating those tricks and secrets (as she calls it) at home. Using her interactive slides I managed to recreate a good deal of what she’s shown, but sometimes I wished I had some sort of documentation ready. Well, this is now available with her book “CSS Secrets”.</p> Deconfusing Pre- and Post-Processing 2015-08-13T00:00:00+00:00 http://www.fettblog.eu/deconfusing-pre-and-postprocessing <p>Some sort of follow-up to my first Medium article – <a href="https://medium.com/@ddprrt/postcss-misconceptions-faf5dc5038df">PostCSS Misconceptions</a> – this one deals with the terms pre- and post-processing in general. Well illustrated and with a verdict.</p> How we sped up ruxit.com 2015-08-12T00:00:00+00:00 http://www.fettblog.eu/how-we-sped-up-ruxit-com <p>The first time I wrote something for my new Job at <a href="https://ruxit.com">Ruxit.com</a>. We spent about three weeks to push the site’s loading performance to a possible max, without us having the possibility to reduce the page’s weight. <a href="https://blog.ruxit.com/how-we-sped-up-ruxit-com/">This is the write-up of our endeavour</a>.</p> PostCSS Misconceptions 2015-08-11T00:00:00+00:00 http://www.fettblog.eu/postcss-misconceptions <p>I tried out Medium as a publishing platform recently. The result is a short rant on how people few PostCSS. While I love the tool and use it everyday, I do want to make clear what’s it all about. Have fun <a href="https://medium.com/@ddprrt/postcss-misconceptions-faf5dc5038df">reading</a>.</p> beyond tellerrand 2015 2015-05-16T00:00:00+00:00 http://www.fettblog.eu/beyond-tellerrand-2015 <p>Last week I attended this year’s <a href="http://beyondtellerrand.com/">beyond tellerrand</a> in Düsseldorf. It was now my third or fourth time (memories are actually really fuzzy … but then again you don’t remember how often you came back to your home during a year) and again I was blown away by both the quality of the conference and the warmth and open-mindedness of its audience.</p> Gulp Recipes - Part Two: You might not need this plugin 2015-05-03T00:00:00+00:00 http://www.fettblog.eu/gulp-recipes-part-2 <p>One month has passed, many questions on <a href="http://stackoverflow.com/questions/tagged/gulp">StackOverflow</a> have been answered, so here’s yet another round of common Gulp issues with a simple and repeatable solution to them. Be sure to check out last <a href="/gulp-recipes-part-1">month’s edition</a>, as well as my articles on <a href="/php-browsersync-grunt-gulp">Gulp, PHP and BrowserSync</a> and <a href="/gulp-browserify-multiple-bundles/">multiple Browserify bundles</a>.</p> Interview with bugtrackers.io 2015-04-27T00:00:00+00:00 http://www.fettblog.eu/interview-with-bugtrackers <p>I had the sincere pleasure of doing an interview with the guys at <a href="https://www.bugtrackers.io/interview-stefan-baumgartner">bugtrackers.io</a>. We talked a lot about my journey as a web developer for the last 15 years. Starting as a teenage boy doing <a href="http://web.archive.org/web/20040403201325/http://www.squarenet.de/main.php?site=sn-news">Gaming websites</a>, up to creating websites professionally with several agencies, and finally my landing at <a href="http://www.ruxit.com">Ruxit</a>.</p> Gulp Recipes - Part One 2015-04-04T00:00:00+00:00 http://www.fettblog.eu/gulp-recipes-part-1 <p>In the last two weeks I spent a good deal of time on <a href="http://stackoverflow.com/questions/tagged/gulp">StackOverflow</a>, trying to solve every open Gulp question there is. The reasons for that are manifold, and besides an overall high amount of spare time and a strong tendency to masochism (it would be more if I’d watch the JavaScript channel there), there was one more reason, which I hope to address at some point in the future.</p> Gulp: Creating multiple bundles with Browserify 2015-03-25T00:00:00+00:00 http://www.fettblog.eu/gulp-browserify-multiple-bundles <p>With the ever-changing eco system of Node.js tools, a short version disclaimer. This article has been created using</p> Running an on-demand PHP server with BrowserSync and Grunt/Gulp 2015-03-21T00:00:00+00:00 http://www.fettblog.eu/php-browsersync-grunt-gulp <p>Quite a while ago I wrote a little article on <a href="/blog/2013/11/17/the-magic-of-grunt-contrib-connect-and-how-to-run-php-with-it/">connect middleware and how to run PHP with it</a>. While the article was originally intended to introduce the concept of connect middlewares to the Grunt audience, I get a lot of feedback on the PHP part. Which was actually broken by design. So, if you’re search for a <em>real</em> on-demand PHP server in your Grunt or Gulp setup, and have all the livereload goodness you know from your connect server, proceed:</p> Revisiting LESS 2015-02-13T00:00:00+00:00 http://www.fettblog.eu/revisting-less <p>Back in 2011 when we started using preprocessors at our company the decision fell very quick to <a href="http://www.lesscss.org">LESS</a>. The reasons for that where mannifold:</p> Frontend-Tooling Workshop in March - Slides inside 2015-01-04T00:00:00+00:00 http://www.fettblog.eu/frontend-tooling <p>I happen to hold my third iteration of the “Frontend Tooling with Grunt and Yeoman” workshop at this years <a href="http://javascript-days.de/2015/">JavaScript Days</a> in March. It’s in Munich, so if you happen to be bear this lovely town, drop by and learn a lot about different JavaScript technologies.</p> My contribution to Christoph Rumpel's "10 things that will make you a better developer" 2014-12-10T00:00:00+00:00 http://www.fettblog.eu/contribution-to-10-things Slides and notes to: Introduction to Yeoman 2014-12-09T00:00:00+00:00 http://www.fettblog.eu/introduction-to-yeoman-slides <p>A few weeks ago I did a short talk on <a href="http://yeoman.io">Yeoman</a> at the Linzer edition of <a href="http://codeweek.eu">Codeweek</a> and how it helps us in our daily workflow. For the first time I tried to have some sort of script, and afterwards I even made notes. So aside from funny images, you even can read what I was talking about. Have fun!</p> Making dragonquest.at Open Source 2014-09-17T00:00:00+00:00 http://www.fettblog.eu/making-dragonquest.at-open-source <p>No tutorials or tech guides today, this is something rather personal! Yesterday while waiting for the launch of Netflix I got an E-Mail that the original <em>Dragon Quest</em> is available for Smartphones, making it the first time that this game makes it to Europe since its original release in 1986.</p> CSS levels up: the HWB colour model 2014-07-13T00:00:00+00:00 http://www.fettblog.eu/hwb <p>HWB is short for “Hue, Whiteness, Blackness” and is a new colour space format, which is now proposed in the current <a href="http://dev.w3.org/csswg/css-color/#the-hwb-notation">CSS Colours Module Level 4</a> working draft.</p> Formular One - or - How to style <select> elements 2014-06-05T00:00:00+00:00 http://www.fettblog.eu/style-select-elements <p>Remember those days where developers made the most amazing forms in Flash because they both had to do everything from scratch due to lack of being close to an operating system. And designers decided to put extra effort in looks in behaviour because of … design?</p> Lesser known Grunt.js features: Renaming of files 2014-05-27T00:00:00+00:00 http://www.fettblog.eu/blog/2014/05/27/undocumented-features-rename <p>Recently I had to deploy some static sites for some client, whose server didn’t allow to automatically redirect to <code class="highlighter-rouge">index.html</code> when accessing a directory. It had to be named <code class="highlighter-rouge">index.php</code> for whatever reason.</p> Gulp, Sass, Autoprefixer, Sourcemaps! 2014-04-10T00:00:00+00:00 http://www.fettblog.eu/blog/2014/04/10/gulp-sass-autoprefixer-sourcemaps <p><strong>Update 2016/02</strong>: <em>This is old. This might not be up to date anymore!</em></p> kod.io Linz 2014-03-02T00:00:00+00:00 http://www.fettblog.eu/blog/2014/03/02/kodio <p>Last Saturday the very first developer conference of Linz was held at the Ars Electronica Center. A place I haven’t visited for 10 years. It was called <a href="http://linz.kod.io">kod.io</a> and was the international spin-off of a Turkish event held last year.</p> noPrefixes flag in Modernizr 2014-02-20T00:00:00+00:00 http://www.fettblog.eu/blog/2014/02/20/no-prefixes <p>More than half a year ago I had some little rant on why we should <a href="/blog/2013/07/02/preparing-for-an-unprefixed-future/">drop using vendor prefixes as a whole</a>. Main points were:</p> Create manageable Sass components (to use with Bower, etc.) 2014-01-13T00:00:00+00:00 http://www.fettblog.eu/blog/2014/01/13/manageable-sass-components <p>Having a set of reusable and ready software components is a great thing. And for a multitude of reasons, like ensuring DRY development or boosting efficiency. A software component is a self contained unit of program code which can be accessed only by a defined interface. Or like <a href="http://www.eecs.berkeley.edu/~newton/Classes/EE290sp99/lectures/ee290aSp994_1/tsld009.htm">Berkely University puts it</a>:</p> Remake, Remodel! Part Three: How to switch from Wordpress to Jekyll 2014-01-02T00:00:00+00:00 http://www.fettblog.eu/blog/2014/01/02/how-to-switch-from-wordpress-to-jekyll <p>Even tough I was pretty satisfied with my old blog, and especially its design, I felt the need to go into another round of updating not only the look, but more importantly the system behind it. So, even if everything looks shiny and new (more likely: crappy), this is all about the nuts and bolts behind the content: <strong>Converting your blog from Wordpress to Jekyll</strong></p> The magic of grunt-contrib-connect, and how to run PHP with it 2013-11-17T00:00:00+00:00 http://www.fettblog.eu/blog/2013/11/17/the-magic-of-grunt-contrib-connect-and-how-to-run-php-with-it <p><strong>Note:</strong> <em>This article is rather old. If you want to know more about `connect`, proceed, if you just want to have a PHP sever with livereload for your Grunt or Gulp setup, go <a href="/php-browsersync-grunt-gulp/">there</a></em> Topconf Tallinn 2013 2013-11-13T00:00:00+00:00 http://www.fettblog.eu/blog/2013/11/13/topconf-tallinn-2013 <p>Last week I attended <a href="http://topconf.com">Topconf</a> in Tallinn, Estonia. It was my very first time in Estonia and also my first time at a conference which wasn't targeted to web developers or designers. Instead, it was a software conference which appealed both hardcore coders and project managers.</p> <p>And it surprised me, big time! Not completely unrelated to this topics, I still consider myself a rookie in "Agile" and Co., and liked getting some new ideas from the pros. On the other hand I found myself really contributing with a view on the web as an app platform topic, and added a good chunk of fun with the current state of mobile browsers. I like the new intro of my talks which always catches the audience. Can't wait to show that stuff to the Drupal community next week.</p> <p class="img-holder"><a href="http://pbs.twimg.com/media/BYXoJEmIgAA6Iq9.jpg:large"><img src="http://pbs.twimg.com/media/BYXoJEmIgAA6Iq9.jpg:large" alt="Best conference badge ever" width="512" style="opacity:1 !important" /></a></p> <p>I held "Mobile Browser Games", possibly for the last time ever, and "Keep Calm and Browse Happy", which showed some tips and techniques to speed up your web app on mobile phones without losing the spirit of Progressive Enhancement. I really enjoyed having a panel discussion with Max Firtman. Though being in the same camp with our opinions, we shared a lot of new ideas with an interested audience. Would love to do that again in the future.</p> <p>I spent a lot of time with the fellow speakers from abroad and had a blast hanging out with them in sessions, between sessions and after sessions. Mostly with Christoph Engelbert from Germany, who was really funny and a great chap to be with. I had no clue on his talking topics whatsoever, but nonetheless enjoyed his enthusiasm in working, speaking and of course chatting.</p> <p>Also enjoyed a classy "My programming language is better than yours" discussion from time to time, especially with "Hattori Honza" Kral - who insists people who like JavaScript suffer from "Stockholm Syndrome" (nice one, Honza) - and Nigel Runnels. We coined the term "MontyScript", so if you find a Python preprocessor in the future for you to abandon all sanity, it might have this very name!</p> <p class="img-holder"><a href="http://pbs.twimg.com/media/BYe_FaOIUAAdczI.jpg:large"><img src="http://pbs.twimg.com/media/BYe_FaOIUAAdczI.jpg:large" alt="Conference after party" width="512" style="opacity:1 !important"/></a></p> <p>Also in the bunch was G&aacute;bor T&ouml;r&ouml;k. He not only shared some intriguing ideas with, but also proved to be a really nice guy with whom you can enjoy some new beers and have a good laugh. Bro, I'll owe you a drink. Budapest is just a stone throw away from Linz, so prepare for one or more visits in the future!</p> <p>One of the greatest talks for me was given by Vladimir Agafonkin. If you don't know him, you sure know his work Leaflet.js, the one and only alternative to Google Maps. His talk about spending a good chunk of one's time on Open Source projects and having a lot of success was very inspiring. Plus, he's one of the nicest and most relaxed guys I've ever met.</p> <p class="img-holder"><a href="http://pbs.twimg.com/media/BYeNtxXIUAAVMB_.jpg"><img src="http://pbs.twimg.com/media/BYeNtxXIUAAVMB_.jpg" alt="Vladimir talking Leaflet" width="512" style="opacity:1 !important"></a></p> <p>Rachel Laycock not only caught me with her talk on Continuous Delivery, but also with her more personal story of being a professional homeless by living on every corner of the globe. Intriguing story, Rachel, you should sometime talk about just that!</p> <p>Oh, and Lukas Eder: Too bad we met at the very last minute of Topconf, next time we have to chat a lot more! Same goes for Johan Andr&eacute;n, who coined the term "abstract hangover". It's exactly what you think it is!</p> <p>I really hope to see everyone I met once again at a conference or somewhere else. Had a great time with a lot of great people and was pumped with energy and ideas when I came back.</p> <p class="img-holder"><a href="http://pbs.twimg.com/media/BYXw7lMIgAAuxbg.jpg"><img src="http://pbs.twimg.com/media/BYXw7lMIgAAuxbg.jpg" width="512" alt="Mobile track with Max Firtman and me" style="opacity:1 !important"/></a></p> <p>With all the different worlds in software coming closer and closer to each other I really like the idea of talking again about browsers and the web in front of a software engineering audience. Topconf is with its broad scope a great place to get new ideas and truly take a peek beyond your own nose. Chris Frei did a great job in organising the conference and I would love to be part of it some time in the future!</p> <p class="img-holder"><a href="http://pbs.twimg.com/media/BYaFKdhIEAAuZwr.jpg:large"><img src="https://pbs.twimg.com/media/BYaFKdhIEAAuZwr.jpg:large" alt="Christoph and me having fun" width="512"></a></p> <p>This week there will be a full round up of this conference at <a href="http://netural.com">Netural</a>, check it out if you want a more content related and less personal view of things.</p> <p><em>Image credits: @processpirate @sleepyfox @noctarius2k @ddprrt</em></p> Content vs. value 2013-10-21T00:00:00+00:00 http://www.fettblog.eu/blog/2013/10/21/content-vs-value <p>Title's for all content strategists out there who expect something really meta and with a lot of additional bla bla. Well, this is about HTML Content vs. Input Values.</p> Great Scott! Five lesser known shortcuts for Emmet.io that rock! 2013-10-16T00:00:00+00:00 http://www.fettblog.eu/blog/2013/10/16/great-scott-five-lesser-known-shortcuts-for-emmet-io-that-rock <p>I'm a huge fan of <a href="http://emmet.io">Emmet</a>. It allows you not only to create markup in no-time by using a CSS-like syntax in your most favourite code editor (e.g. <code>ul&gt;li*5</code> expands to one <code>&lt;ul&gt;</code> element with five nested <code>&lt;li&gt;</code> elements in HTML after hitting the tab key. Alternatively, you can just type <code>m20</code> in your CSS to get <code>margin: 20px</code> after hitting tab).</p> It's all about the content! 2013-10-11T00:00:00+00:00 http://www.fettblog.eu/blog/2013/10/11/its-all-about-the-content <p>You know parallel scrolling websites? The kind where big sunglasses assemble themselves, break through rocks and crash your browser? The kind everyone loves. Except developers. Today I found <a href="http://thereisnopagefold.com">thereisnopagefold.com</a> by <a href="http://twitter.com/coda_za">Damien du Toit</a>, who had his own opinion on the whole thing. Check it out.</p> Digital Visions 2013 2013-10-10T00:00:00+00:00 http://www.fettblog.eu/blog/2013/10/10/digital-visions-2013 <p>Last friday <a href="http://liechtenecker.at">J&uuml;rgen Liechtenecker and company</a> held the Digital Visions conference in Vienna for the second time. I was invited to speak and got the amazing opportunity to try out a new talk in front of an interested crowd. Digital Visions quickly became the no. 1 meeting point for all Front End devs and UX people in Austria, and I was not only happy to see a lot of familiar faces from Linz and Vienna, but also meet cab sharing buddy <a href="http://maddesigns.de">Sven Wolfermann</a> again. He's an amazing speaker and combines true "Berliner Schnauze" with a lot of interesting information. Have fun in Amsterdam, pal, see you in D&uuml;sseldorf at the latest!</p> Using grunt-connect-proxy 2013-09-20T00:00:00+00:00 http://www.fettblog.eu/blog/2013/09/20/using-grunt-connect-proxy <p> <em>Update 2014/01/13: The interface of <code>grunt-contrib-connect</code> has slightly changed. Please check <a href="/blog/2013/11/17/the-magic-of-grunt-contrib-connect-and-how-to-run-php-with-it/">my article on how to add middleware</a>. The proxy middleware is still the same, tough.</em> </p> <p>With any application that communicates with some sort of backend interface exchanging JSON data, you're often in the need to use proxies in your own server configuration to overcome CORS restrictions (either that, or use jsonp, which always seems like a workaround to me). Previously --- in the times we worked with our local Apache servers -- it was always a bit tedious and also caused a lot of time running into the project setup for every team member. Now with our yeoman-grunt-bower setup, it mostly takes no longer than a minute. Cause guess what, there's a already Grunt-task for that.</p> <!--more--> <h2>The problem</h2> <p>Why do we need proxies? Well, I assume your app uses JSON for data transfer, so by calling an URL like <code>http://someserver.com/API/Login</code> you get all the data you need for your JavaScript application. And in most cases your application is located on the same server, which makes those calls really easy. However, if your are developing locally on your machine and try to call this server, you'll soon see that you won't come very far. Due to <a href="http://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a> restrictions you are not allowed to retrieve data from a different domain than yours.</p> <p>Proxies help with that. They stand in for the server you want to reach, get the required data, while letting your browser think that you're all in the right domain. So instead by calling <code>http://someserver.com/API</code> you can call <code>http://localhost/API</code>.</p> <h2>Creating a proxy with Grunt</h2> <p>Task-runner Grunt has quickly become my most favorite tool when it comes to front-end development. It builds up on Node.js, which allows me to easily create new tasks, plus there are already a lot of great tasks out there which help me with my everyday development. Also for proxies, like the one created by <a href="https://github.com/drewzboto/grunt-connect-proxy">Drewzboto</a>.</p> <p>Recently we switched our scaffolding process from a GitHub clone clusterf**k to a more streamlined process with Bower and Yeoman, and Yeoman heavily uses Grunt. So this short tutorial will show you how to add a proxy to your already existing Yeoman-like Gruntfile (with <code>connect</code> and <code>livereload</code> already set in place.</p> <h3>Installing</h3> <p>Coulnd't be easier. Just type</p> <pre><code class="language">npm install --save-dev grunt-connect-proxy </code></pre> <p>With the parameter <code>--save-dev</code> the module gets added to your <code>package.json</code>, the one file you have to make available in your code repository for your co-developers.</p> <h3>Setting up a proxy</h3> <p>Search your <code>connect</code> task in your Gruntfile and add this little snippet:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">connect</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">server</span><span class="p">:</span> <span class="p">{</span> <span class="nl">proxies</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">context</span><span class="p">:</span> <span class="s1">'/'</span><span class="p">,</span> <span class="na">host</span><span class="p">:</span> <span class="s1">'someserver.com'</span><span class="p">,</span> <span class="na">changeOrigin</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">]</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>This snippet tells us, that any call which can't be answered by your machine will be forwared to <code>someserver.com</code>. There are a lot more options, you might also need <code>https</code> or <code>port</code> parameters for instance. Check out <a href="https://github.com/drewzboto/grunt-connect-proxy">the GitHub repo</a> for more information on that.</p> <p>Add this part to the top of your <code>Gruntfile.js</code>, right before <code>module.exports</code>.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">proxySnippet</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'grunt-connect-proxy/lib/utils'</span><span class="p">).</span><span class="nx">proxyRequest</span><span class="p">;</span></code></pre></figure> <p>and call the <code>proxySnippet</code> middleware in your <code>livereload</code> configuration:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">connect</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">livereload</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">middleware</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">connect</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span> <span class="nx">lrSnippet</span><span class="p">,</span> <span class="nx">mountFolder</span><span class="p">(</span><span class="nx">connect</span><span class="p">,</span> <span class="s1">'.tmp'</span><span class="p">),</span> <span class="nx">mountFolder</span><span class="p">(</span><span class="nx">connect</span><span class="p">,</span> <span class="nx">yeomanConfig</span><span class="p">.</span><span class="nx">app</span><span class="p">),</span> <span class="nx">proxySnippet</span> <span class="p">];</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Now you're all set up. The last thing you've to do is to call <code>configureProxies</code> before using <code>connect</code> in your task, and everything's ready to go:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">grunt</span><span class="p">.</span><span class="nx">task</span><span class="p">.</span><span class="nx">run</span><span class="p">([</span> <span class="s1">'clean:server'</span><span class="p">,</span> <span class="s1">'concurrent:server'</span><span class="p">,</span> <span class="s1">'configureProxies'</span><span class="p">,</span> <span class="s1">'connect:livereload'</span><span class="p">,</span> <span class="s1">'open'</span><span class="p">,</span> <span class="s1">'watch'</span> <span class="p">]);</span></code></pre></figure> <p>It's as easy as that. We added this to our main Gruntfile we use for scaffolding our projects, so everytime we need a proxy, it's just peace of cake instead of endless subdomain and subserver configuration in the old <code>httpd-vhosts.conf</code> file.</p> <h2>Directing to more than one server</h2> <p>This was actually a part which required a lot more thinking and understanding of proxies to get this done right. We had the -- at least for us -- rather specific use case to retrieve our data from more than one server at the same time. Mainly because there was one server handling the content, and one server which took care of all the user data. As you can see, the <code>proxies</code> option in the default task is actually an array, which means we can easily define more than one proxy configuration.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">connect</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">server</span><span class="p">:</span> <span class="p">{</span> <span class="nl">proxies</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">context</span><span class="p">:</span> <span class="s1">'/user'</span><span class="p">,</span> <span class="na">host</span><span class="p">:</span> <span class="s1">'userserver.com'</span><span class="p">,</span> <span class="na">changeOrigin</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="p">{</span> <span class="na">context</span><span class="p">:</span> <span class="s1">'/'</span><span class="p">,</span> <span class="na">host</span><span class="p">:</span> <span class="s1">'someserver.com'</span><span class="p">,</span> <span class="na">changeOrigin</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">]</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Creating the new context <code>user</code> which should allow me to use <code>http://localhost:9000/user</code> to get to <code>http://userserver.com</code>. I put that one before the other on purpose, cause at the moment this will actually do nothing at all.</p> <p>Reason is that the proxy can't decide why to redirect all the <code>user</code> calls to the other service. We need to define a redirection for the original proxy to make this happen:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">connect</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">server</span><span class="p">:</span> <span class="p">{</span> <span class="nl">proxies</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">context</span><span class="p">:</span> <span class="s1">'/user'</span><span class="p">,</span> <span class="na">host</span><span class="p">:</span> <span class="s1">'userserver.com'</span><span class="p">,</span> <span class="na">changeOrigin</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="p">{</span> <span class="na">context</span><span class="p">:</span> <span class="s1">'/'</span><span class="p">,</span> <span class="na">host</span><span class="p">:</span> <span class="s1">'someserver.com'</span><span class="p">,</span> <span class="na">changeOrigin</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">rewrite</span><span class="p">:</span> <span class="p">{</span> <span class="s1">'/user'</span> <span class="p">:</span> <span class="s1">'/user'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">]</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>This rewrite rule tells the proxy that ever call which starts with <code>user</code> gets redirected to the <code>user</code> context we defined earlier. It won't work the other way round, since the context has to be defined beforehand, but that shouldn't matter actually. Feel free to include as many proxies and rewrites. Using this structure everything should work fine.</p> <h2>Bottom line</h2> <p>I've to admit that I've grown on Grunt. Not only that everything just seams so easy to create, it's also one definition that works on every workstation in our department. No need for petty set up phases, just everything is on the go and ready if another team member joins a project. Proxies have been the crux for each project, often leading us to switch to JSONP files just for convenience, and because the Backend guys could actually implement that much, much quicker. No need for that anymore.</p> <h2>Update</h2> In the meantime Grunt's interfaces have changed and Yeoman's generators use the new <code>grunt-contrib-connect package</code> instead of the deprecated <code>grunt-connect</code> one that has been used before. This package includes livereload as a default, though it's not that easy anymore to add middleware than it was before. I addressed this topic in my recent article on <a href="http://www.fettblog.eu/blog/2013/11/17/the-magic-of-grunt-contrib-connect-and-how-to-run-php-with-it/">The magic behind grunt-contrib-connect</a>. Check it out and add your proxySnippet where it belongs! Using assemble.io with yeoman.io's webapp Gruntfile 2013-09-02T00:00:00+00:00 http://www.fettblog.eu/blog/2013/09/02/using-assemble-io-with-yeoman-ios-webapp-gruntfile <p>With <a href="http://h5bp.github.io/Effeckt.css/dist/">Effeckt.css</a> I discovered <a href="http://assemble.io/">assemble.io</a>, a node-based static site generator for ... well ... assembling HTML files from different parts.</p> <p>A tool like that was much needed by our department once we switched from an inconvenient clutter of Ant builds, PHP includes, CodeKit and command line calls to our yeoman/grunt setup, so I gave it a try. And I was stunned how easy the set up was and how perfect it fits into the environment we created based upon yeoman's web app generator.</p> <p>In this short article, I'll show you how you can use assemble.io with Handlebars templates and the basic yeoman.io web app generator Gruntfile.</p> <!--more--> <h2>The Basics</h2> <p>When compiling, assemble.io clutches together bits and pieces from three different resource groups:</p> <ul> <li>Layouts: The basic layouts, with all the basic HTML setup, navigations, wrappers, and so on.</li> <li>Pages: Every page without the clutter around it. Just the "content" of a site</li> <li>Partials: Reusable Modules that can be included with different parameters on any of the above. Partials can also include other partials.</li> </ul> <p>The assembling process goes as follows: Every page is included into a layout, with partials completing everything in-between.</p> <h3>Layouts</h3> <p>A basic layout can be like that:</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="cp">&lt;!doctype html&gt;</span> <span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span> <span class="nt">&lt;head&gt;</span> <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span> <span class="c">&lt;!-- the title from the page --&gt;</span> <span class="nt">&lt;title&gt;</span>{{title}}<span class="nt">&lt;/title&gt;</span> <span class="nt">&lt;/head&gt;</span> <span class="nt">&lt;body&gt;</span> <span class="c">&lt;!-- Include a nav from partials --&gt;</span> {{&gt;nav}} <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"wrapper"</span><span class="nt">&gt;</span> <span class="c">&lt;!-- The body of the page --&gt;</span> {{&gt;body}} <span class="nt">&lt;/div&gt;</span> <span class="c">&lt;!-- Another partial --&gt;</span> {{&gt;footer}} <span class="nt">&lt;/body&gt;</span> <span class="nt">&lt;/html&gt;</span></code></pre></figure> <p>We already included some partials (navigation and footer), as well as some parts we define later in the pages. Either with parameters (title) or with content (body).</p> <h3>Pages</h3> <p>With the whole HTML layout in the layout modules, a page is a lot cleaner. It defines parameters in a sort of setup header, after that there's the content that is included when calling <code class="language-stuff"></code></p> <figure class="highlight"><pre><code class="language-html" data-lang="html">--- title: About --- <span class="nt">&lt;h1&gt;</span>About everything<span class="nt">&lt;/h1&gt;</span> <span class="nt">&lt;p&gt;</span>Lorem Ipsum is not good for content<span class="nt">&lt;/p&gt;</span> <span class="nt">&lt;ul&gt;</span> ... <span class="nt">&lt;/ul&gt;</span></code></pre></figure> <p>Easy!</p> <h3>Partials</h3> <p>I guess you get the idea by now... I'll cover the usage of parameters and stuff sometime later. This is all about the grunt setup.</p> <h3>Folder structure</h3> <p>For our websites, those module categories do have separated folders in a template folder in our app directory:</p> <pre><code>app |-- templates |-- layouts |-- pages |-- partials </code></pre> <h2>Adding it to yeoman.io's webapp Gruntfile</h2> <p>yeoman.io's webapp setup is the best way to start any web related project. Plus, the generator can be easily adapted to be used with your workflow in particular (I guess this will also be an article for later).</p> <p>Anyhow: The cool thing with its Gruntfile is, that you not only get compiling and building done, but also have some sort of developing environment, where you can easily access all the files in it's plain source, unminified and as-is. We will now setup grunt for both the building process, as well as the "grunt server" task for your dev environment.</p> <h3>assemble.io setup</h3> <p>First of all: Be sure to install assemble correctly after scaffolding your web app:</p> <pre><code>yo webapp npm install --save-dev assemble </code></pre> <p>Open your Gruntfile.js, and add assemble right after defining the module:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">grunt</span><span class="p">)</span> <span class="p">{</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">'assemble'</span><span class="p">);</span> <span class="p">...</span> <span class="p">};</span></code></pre></figure> <p>With that done, we can do the basic setup for the assemble task. Just add this part anywhere inside the <code class="language-stuff">.initConfig</code> scope:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">assemble</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">flatten</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">layout</span><span class="p">:</span> <span class="s1">'layout.hbs'</span><span class="p">,</span> <span class="nx">layoutdir</span><span class="p">:</span> <span class="s1">'&lt;%= yeoman.app %&gt;/templates/layouts'</span><span class="p">,</span> <span class="nx">assets</span><span class="p">:</span> <span class="s1">'dist/images'</span><span class="p">,</span> <span class="nx">partials</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= yeoman.app %&gt;/templates/partials/*.hbs'</span><span class="p">]</span> <span class="p">},</span> <span class="p">...</span> <span class="p">}</span></code></pre></figure> <p>Pretty self-explanatory. We'll define the location of layout, partials, and some other stuff which you can ignore for now. Note that partials can be an array of folders, make use of that.</p> <h3>grunt build</h3> <p>Before we'll check on the (oh so sweet) on the fly compiling when spawning a project dependent server, we just check on how to compile this baby when creating a build.</p> <p>With the setup being complete, just add a line for the "dist" target. This is yeoman's default target for anything building and distribution related.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">assemble</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">flatten</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">layout</span><span class="p">:</span> <span class="s1">'layout.hbs'</span><span class="p">,</span> <span class="nx">layoutdir</span><span class="p">:</span> <span class="s1">'&lt;%= yeoman.app %&gt;;/templates/layouts'</span><span class="p">,</span> <span class="nx">assets</span><span class="p">:</span> <span class="s1">'dist/images'</span><span class="p">,</span> <span class="nx">partials</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= yeoman.app %&gt;;/templates/partials/*.hbs'</span><span class="p">]</span> <span class="p">},</span> <span class="nx">dist</span><span class="p">:</span> <span class="p">{</span> <span class="nl">files</span><span class="p">:</span> <span class="p">{</span> <span class="s1">'&lt;%= yeoman.dist %&gt;;/'</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= yeoman.app %&gt;;/templates/pages/*.hbs'</span><span class="p">]</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>We have defined layouts and partials in the setup, the dist-target tells us where pages are located and where the assembled output should land. It's pretty straightforward: Just put all the pages in <code class="language-stuff">&lt;%= yeoman.dist %&gt;/</code>, the output directory of the build process.</p> <p><strong>Note:</strong> This can create some conflicts if you're using the <code>htmlmin</code> task, since it tries for itself to copy everything HTML related from <code class="language-stuff">&lt;%= yeoman.app %&gt;/</code> to <code class="language-stuff">&lt;%= yeoman.dist %&gt;/</code>. Just put everything done by assemble into a temporary directory, and let <code class="language-stuff">htmlmin</code> (which strangely never worked for me...) fetch the files from there:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">assemble</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">flatten</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">layout</span><span class="p">:</span> <span class="s1">'layout.hbs'</span><span class="p">,</span> <span class="nx">layoutdir</span><span class="p">:</span> <span class="s1">'&lt;%= yeoman.app %&gt;/templates/layouts'</span><span class="p">,</span> <span class="nx">assets</span><span class="p">:</span> <span class="s1">'dist/images'</span><span class="p">,</span> <span class="nx">partials</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= yeoman.app %&gt;/templates/partials/*.hbs'</span><span class="p">]</span> <span class="p">},</span> <span class="nx">dist</span><span class="p">:</span> <span class="p">{</span> <span class="nl">files</span><span class="p">:</span> <span class="p">{</span> <span class="s1">'.tmp'</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= yeoman.app %&gt;/templates/pages/*.hbs'</span><span class="p">]</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">htmlmin</span><span class="p">:</span> <span class="p">{</span> <span class="nl">dist</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="p">},</span> <span class="nx">files</span><span class="p">:</span> <span class="p">[{</span> <span class="na">expand</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">cwd</span><span class="p">:</span> <span class="s1">'.tmp'</span><span class="p">,</span> <span class="na">src</span><span class="p">:</span> <span class="s1">'*.html'</span><span class="p">,</span> <span class="na">dest</span><span class="p">:</span> <span class="s1">'&lt;%= yeoman.dist %&gt;'</span> <span class="p">}]</span> <span class="p">}</span> <span class="p">},</span></code></pre></figure> <p>Voil&aaucte;, you're done.</p> <h3>grunt server</h3> <p>Now for the fun part: Having it compiled on the fly while changing your source files, and live reload it in your browser.</p> <p>This part of the yeoman Gruntfile actually got me interested into the scaffolding tool in the first place, since it's a LOT more convenient to have your changes displayed directly after doing them. No matter if it's in Sass, HTML or JavaScript.</p> <p>To achieve that, grunt creates a TMP directory where it puts all compiled stuff into. The server also points to that directory. So for having our HTML files assembled, just add this line of code to our assemble setup:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">assemble</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">server</span><span class="p">:</span> <span class="p">{</span> <span class="nl">files</span><span class="p">:</span> <span class="p">{</span> <span class="s1">'.tmp/'</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= yeoman.app %&gt;/templates/pages/*.hbs'</span><span class="p">]</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>If you start your server the first time, all pages are compiled and available. If you want to update your server anytime a <code class="language-stuff">*.hbs</code> file changes, add the following line to the <code class="language-stuff">watch</code> task:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">watch</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">assemble</span><span class="p">:</span> <span class="p">{</span> <span class="nl">files</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= yeoman.app %&gt;/templates/layouts/*.hbs'</span><span class="p">,</span> <span class="s1">'&lt;%= yeoman.app %&gt;/templates/pages/*.hbs'</span><span class="p">,</span> <span class="s1">'&lt;%= yeoman.app %&gt;/templates/partials/*.hbs'</span><span class="p">],</span> <span class="nx">tasks</span><span class="p">:</span> <span class="p">[</span><span class="s1">'assemble:server'</span><span class="p">]</span> <span class="p">}</span> <span class="p">},</span> <span class="p">...</span></code></pre></figure> <p>Which actually just says: If anything changed in our layout/pages/partials folder, execute the assemble task again!</p> <p>To have the changes displayed without refreshing, tell livereload to have a look at the HTML files compiled into the TMP directory of the server:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">watch</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">livereload</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">livereload</span><span class="p">:</span> <span class="nx">LIVERELOAD_PORT</span> <span class="p">},</span> <span class="nx">files</span><span class="p">:</span> <span class="p">[</span> <span class="s1">'.tmp/*.html'</span><span class="p">,</span> <span class="c1">// Add this </span> <span class="p">...</span> <span class="p">]</span> <span class="p">}</span> <span class="p">},</span></code></pre></figure> <p>And you're done! Enjoy!</p> <h2>Updates</h2> <p>I got some great feedback in the comments. Thank you guys, you're amazing! Here are some issues I forgot to address:</p> <p>Peter pointed out that with the current setup, <code>usemin</code> won't be able to run through your code and compile scripts and style files. You can either point the <code>useminPrepare</code> task to one of the template or partial files, or you just direct them to the <code>.tmp</code> directory:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">useminPrepare</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">dest</span><span class="p">:</span> <span class="s1">'&lt;%= yeoman.dist %&gt;'</span> <span class="p">},</span> <span class="nx">html</span><span class="p">:</span> <span class="s1">'.tmp/index.html'</span> <span class="p">},</span></code></pre></figure> <p>Just run <code>useminPrepare</code> after <code>assemble</code>, which I forgot to add anyways. Thanks Sarith for pointing me onto that one!</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">...</span> <span class="nx">concurrent</span><span class="p">:</span> <span class="p">{</span> <span class="nl">server</span><span class="p">:</span> <span class="p">[</span> <span class="s1">'compass'</span><span class="p">,</span> <span class="s1">'jst'</span><span class="p">,</span> <span class="s1">'assemble'</span> <span class="p">],</span> <span class="nx">dist</span><span class="p">:</span> <span class="p">[</span> <span class="s1">'compass'</span><span class="p">,</span> <span class="s1">'jst'</span><span class="p">,</span> <span class="s1">'assemble'</span><span class="p">,</span> <span class="s1">'imagemin'</span><span class="p">,</span> <span class="s1">'svgmin'</span> <span class="p">]</span> <span class="p">}</span> <span class="p">...</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">registerTask</span><span class="p">(</span><span class="s1">'build'</span><span class="p">,</span> <span class="p">[</span> <span class="s1">'clean:dist'</span><span class="p">,</span> <span class="s1">'concurrent:dist'</span><span class="p">,</span> <span class="s1">'useminPrepare'</span><span class="p">,</span> <span class="s1">'concat'</span><span class="p">,</span> <span class="s1">'cssmin'</span><span class="p">,</span> <span class="s1">'uglify'</span><span class="p">,</span> <span class="s1">'copy:dist'</span><span class="p">,</span> <span class="s1">'usemin'</span> <span class="p">]);</span></code></pre></figure> <p>Thanks again for the great feedback! I really appreciate it!</p> <p>As Mark Paul pointed out, you need some updates in your usemin configuration, otherwise you won't get any <code>rev</code> updates:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Performs rewrites based on rev and the useminPrepare configuration </span> <span class="p">...</span> <span class="nx">usemin</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">assetsDirs</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= config.dist %&gt;'</span><span class="p">,</span> <span class="s1">'&lt;%= config.dist %&gt;/images'</span><span class="p">]</span> <span class="p">},</span> <span class="nx">html</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= config.dist %&gt;/{,*/}*.html'</span><span class="p">,</span> <span class="s1">'.tmp/{,*/}*.html'</span><span class="p">],</span> <span class="nx">css</span><span class="p">:</span> <span class="p">[</span><span class="s1">'&lt;%= config.dist %&gt;/styles/{,*/}*.css'</span><span class="p">]</span> <span class="p">}</span> <span class="p">...</span></code></pre></figure> <p>Thank you for mentioning!</p> <h2>Bottom line</h2> <p>Grunt and yeoman are found their way into our development process rather quickly, and I think assemble.io will also be a part in our development future.</p> <p>Setting up was rather easy and took me -- including research on how to use this in the first place -- about half an our. Plus, assemble.io solves major issues we faced after switching to yeoman+grunt:</p> <ul> <li>PHP includes weren't possible anymore, with assemble we can put template parts in separated files again</li> <li>With the much cleaner Handlebars syntax it's possible to prepare those modules for a later refining, e.g. to produce TYPO3/Wordpress/Name-your-CMS modules.</li> </ul> <p>If your website deployment is based upon yeoman and/or grunt, you should definitely give it a try.</p> Basic SVG path tweening with SMIL 2013-07-16T00:00:00+00:00 http://www.fettblog.eu/blog/2013/07/16/basic-svg-path-tweening-with-smil <style>@media(max-width:500px){.responsive-svg{width:100%;}.responsive-svg path{-webkit-transform:scale(0.5); transform:scale(0.5);}}.note{display:none;}.sorrynote{background-color: #c84941; color:white;display:block;}</style> <p id="feature-test2" class="note">Sorry, your browser does not support SVG animations with SMIL.<script>var el=document.getElementById('feature-test2');try{var n=document.createElementNS('http://www.w3.org/2000/svg', 'animate'); if(!n){ el.className+=" sorrynote";}}catch(e1){el.className+=" sorrynote";}</script></p> <p>I'm working on a tribute to one of my childhood heroes, the Caped Crusader, the Dark Knight, the world's greatest detective: Batman. And when I say childhood hero, I do mean a hero to this day. Anyhow, inspired by an EPS file i got over on DeviantArt, I wanted to create a history of his emblems from the very first to the very last, spanning all 73 years, much like <a href="http://www.youtube.com/watch?v=AKR_4kRDR9s" target="_blank">this now infamous video</a> did.</p> <p>First I had the idea of just fading over the logos, but that's actually kinda boring, so I went back to a rad idea I used once back then when Macromedia Flash 4 was still in its early days: Tweening! (well, just like in the video, no?)</p> <p>After a little research, I stumbled upon two ways to do it: Animating SVG with Rapha&euml;lJS, a JavaScript library for cross-browser SVG, or using the very powerful <strong>SMIL for SVG animations</strong>.</p> <p>All right! To the Batcave, Robins!</p> <!--more--> <p class="img-holder"><img style="width: 80%; height: auto" src="http://www.fettblog.eu/wp-content/uploads/2013/07/bat63.svg" /></p> <h2>A short thought on Rapha&euml;lJS</h2> <p>We already have some experience with <a href="http://raphaeljs.com/">Rapha&euml;lJS</a> in our company. We used the library to create parts of <a href="http://www.customize-eyewear.com">Adidas Customize</a> to achieve recolorable, complex formed widgets on IE7 and IE8.</p> <p>The library also allows to animate between paths, and does it in a very interesting, jQuery-like way: Instead of using SMIL, Rapha&euml;lJS interpolates path points between the start and ending state and constantly updates the path inside your SVG. I was stunned by the complexity of this rather powerful algorithm, but looking at it from a performance point of view ... nah, you get the same issues you love to hate from jQuery.</p> <p>Rapha&euml;lJS is good if you don't want to delve to deeply into drawing programs or SVG source code, and I used it mainly to apply certain transformations on exiting SVG paths, and then copying the new SVG result. But for my tribute page I dropped it completely and just used it as a fallback for IE, because SMIL is still not implemented and looking at the current preview of IE11, will not be landing there for quite some while.</p> <p>But the main reason for me to use SMIL was a rather clear one: Why using an 80kb JavaScript library if I can do everything with native means?</p> <h2>The first animation</h2> <p>My work is based on a great animation done by <a href="http://tavmjong.free.fr/blog/?p=741" target="_blank">Tavmjong Bah</a>. In his blog post he give additional information on how he actually implemented it. Some important parts were: The SVG paths you want to transform have to be in the same pattern, otherwise you don't get any animation at all:</p> <p class="img-holder"><svg width="200" height="180"><path fill="#000000" stroke="#000000" d="M89.71882,171.78893C89.71882,171.78893,89.45803999999998,172.95614999999998,95.51977999999997,134.96961999999996C101.57930999999996,96.96831999999999,107.63882999999998,86.47806999999999,113.69879999999995,86.28599999999999C119.76067999999992,86.10869999999998,124.42337999999995,96.49551999999998,127.21909999999997,106.52774999999998C130.01718999999997,116.54519999999998,132.58257999999995,134.96961999999996,132.58257999999995,134.96961999999996C132.58257999999995,134.96961999999996,136.54286999999994,113.75271999999997,138.17623999999995,105.36052999999997C139.80665999999997,96.96831999999996,146.80188999999996,79.48949999999998,152.39422999999994,79.25309999999998C157.99099999999993,79.01669999999997,162.88447999999994,87.88169999999998,166.14679999999993,100.23359999999998C169.41206999999991,112.58549999999998,167.5459899999999,128.66069999999996,167.5459899999999,128.66069999999996C167.5459899999999,128.66069999999996,179.43542999999988,108.62579999999998,179.43542999999988,74.58419999999997C179.43542999999988,40.55736999999998,166.14679999999987,20.862299999999962,166.14679999999987,20.862299999999962C126.52068999999989,45.21149999999996,105.7739299999999,41.72459999999997,105.7739299999999,41.72459999999997C109.26983999999987,16.784399999999962,103.6766199999999,-4.263256414560601e-14,103.6766199999999,-4.263256414560601e-14C103.6766199999999,-4.263256414560601e-14,97.61487999999991,10.711869999999962,97.61487999999991,10.711869999999962C95.51977999999991,8.613819999999961,89.71881999999994,8.39219999999996,89.71881999999994,8.39219999999996C89.71881999999994,8.39219999999996,83.91563999999994,8.613819999999961,81.81891999999993,10.711869999999962C81.81891999999993,10.711869999999962,75.75939999999991,-4.263256414560601e-14,75.75939999999991,-4.263256414560601e-14C75.75939999999991,-4.263256414560601e-14,70.16336999999993,16.784399999999962,73.65971999999994,41.72459999999997C73.65971999999994,41.72459999999997,52.91236999999995,45.21149999999996,13.286559999999952,20.862299999999962C13.286559999999952,20.862299999999962,-5.684341886080802e-14,40.55736999999998,-5.684341886080802e-14,74.58419999999997C-5.684341886080802e-14,108.62579999999996,11.887519999999938,128.66069999999996,11.887519999999938,128.66069999999996C11.887519999999938,128.66069999999996,10.02216999999996,112.58549999999998,13.286559999999952,100.23359999999998C16.550359999999955,87.88169999999998,21.44560999999993,79.01669999999997,27.038839999999936,79.25309999999998C32.63427999999993,79.48949999999998,39.62639999999993,96.96831999999996,41.257409999999936,105.36052999999997C42.890789999999924,113.75271999999997,46.85284999999993,134.96961999999996,46.85284999999993,134.96961999999996C46.85284999999993,134.96961999999996,49.416609999999935,116.54519999999998,52.21454999999992,106.52774999999998C55.01204999999993,96.49551999999998,59.675479999999936,86.10869999999998,65.73485999999991,86.28599999999999C71.79437999999993,86.47806999999999,77.85611999999992,96.96831999999999,83.91563999999994,134.96961999999996C89.97796999999991,172.95614999999998,89.71881999999994,171.78893,89.71881999999994,171.78893" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"><animate dur="3s" repeatCount="indefinite" attributeName="d" values="M89.71882,171.78893C89.71882,171.78893,89.45803999999998,172.95614999999998,95.51977999999997,134.96961999999996C101.57930999999996,96.96831999999999,107.63882999999998,86.47806999999999,113.69879999999995,86.28599999999999C119.76067999999992,86.10869999999998,124.42337999999995,96.49551999999998,127.21909999999997,106.52774999999998C130.01718999999997,116.54519999999998,132.58257999999995,134.96961999999996,132.58257999999995,134.96961999999996C132.58257999999995,134.96961999999996,136.54286999999994,113.75271999999997,138.17623999999995,105.36052999999997C139.80665999999997,96.96831999999996,146.80188999999996,79.48949999999998,152.39422999999994,79.25309999999998C157.99099999999993,79.01669999999997,162.88447999999994,87.88169999999998,166.14679999999993,100.23359999999998C169.41206999999991,112.58549999999998,167.5459899999999,128.66069999999996,167.5459899999999,128.66069999999996C167.5459899999999,128.66069999999996,179.43542999999988,108.62579999999998,179.43542999999988,74.58419999999997C179.43542999999988,40.55736999999998,166.14679999999987,20.862299999999962,166.14679999999987,20.862299999999962C126.52068999999989,45.21149999999996,105.7739299999999,41.72459999999997,105.7739299999999,41.72459999999997C109.26983999999987,16.784399999999962,103.6766199999999,-4.263256414560601e-14,103.6766199999999,-4.263256414560601e-14C103.6766199999999,-4.263256414560601e-14,97.61487999999991,10.711869999999962,97.61487999999991,10.711869999999962C95.51977999999991,8.613819999999961,89.71881999999994,8.39219999999996,89.71881999999994,8.39219999999996C89.71881999999994,8.39219999999996,83.91563999999994,8.613819999999961,81.81891999999993,10.711869999999962C81.81891999999993,10.711869999999962,75.75939999999991,-4.263256414560601e-14,75.75939999999991,-4.263256414560601e-14C75.75939999999991,-4.263256414560601e-14,70.16336999999993,16.784399999999962,73.65971999999994,41.72459999999997C73.65971999999994,41.72459999999997,52.91236999999995,45.21149999999996,13.286559999999952,20.862299999999962C13.286559999999952,20.862299999999962,-5.684341886080802e-14,40.55736999999998,-5.684341886080802e-14,74.58419999999997C-5.684341886080802e-14,108.62579999999996,11.887519999999938,128.66069999999996,11.887519999999938,128.66069999999996C11.887519999999938,128.66069999999996,10.02216999999996,112.58549999999998,13.286559999999952,100.23359999999998C16.550359999999955,87.88169999999998,21.44560999999993,79.01669999999997,27.038839999999936,79.25309999999998C32.63427999999993,79.48949999999998,39.62639999999993,96.96831999999996,41.257409999999936,105.36052999999997C42.890789999999924,113.75271999999997,46.85284999999993,134.96961999999996,46.85284999999993,134.96961999999996C46.85284999999993,134.96961999999996,49.416609999999935,116.54519999999998,52.21454999999992,106.52774999999998C55.01204999999993,96.49551999999998,59.675479999999936,86.10869999999998,65.73485999999991,86.28599999999999C71.79437999999993,86.47806999999999,77.85611999999992,96.96831999999999,83.91563999999994,134.96961999999996C89.97796999999991,172.95614999999998,89.71881999999994,171.78893,89.71881999999994,171.78893;M89.7158785,166.02668C86.3619535,160.60424999999998,80.93657350000001,103.02607999999998,64.1551285,101.47469999999998C56.689320499999994,100.79505,51.4988635,118.52504999999996,51.4988635,118.52504999999996C50.4690455,112.83667999999994,41.6897405,91.14696999999995,26.195198500000004,89.86154999999997C10.702133500000002,88.56134999999995,0.11289030000000277,113.10262999999998,0.11289030000000277,113.10262999999998C0.11289030000000277,113.10262999999998,-1.6940921999999974,77.7165,9.150757500000005,47.76756999999998C19.995608500000007,17.81864999999999,39.105593500000005,1.2854199999999878,39.105593500000005,1.2854199999999878C48.402023500000006,23.492250000000013,67.25049050000001,27.88041999999996,70.86888850000001,29.180619999999976C74.48728550000001,30.466049999999996,73.96868350000001,26.07786999999996,73.96868350000001,26.07786999999996C73.96868350000001,7.224969999999985,84.01272850000001,-5.684341886080802e-14,84.01272850000001,-5.684341886080802e-14C80.8937255,5.156469999999956,84.01272850000001,12.913349999999923,84.01272850000001,12.913349999999923C85.55671550000001,11.361969999999928,87.3917705,11.110799999999927,89.7158785,11.110799999999927C92.0458955,11.110799999999927,93.8809505,11.361969999999928,95.4190285,12.913349999999923C95.4190285,12.913349999999923,98.5439415,5.156469999999899,95.4190285,-5.684341886080802e-14C95.4190285,-5.684341886080802e-14,105.4689815,7.224969999999928,105.4689815,26.07786999999996C105.4689815,26.07786999999996,104.95038149999999,30.46604999999994,108.5687815,29.180619999999976C112.1827415,27.88041999999996,131.0297315,23.492249999999956,140.3261615,1.2854199999999878C140.32716150000002,1.2854199999999878,159.4376315,17.81864999999999,170.28248150000002,47.76756999999998C181.12733150000003,77.7165,179.3203415,113.10262999999998,179.3203415,113.10262999999998C179.3203415,113.10262999999998,168.7296215,88.56134999999995,153.24247150000002,89.86154999999997C137.74793150000002,91.14696999999995,128.9686215,112.83667999999994,127.93437150000003,118.52504999999996C127.93437150000003,118.52504999999996,122.74391150000002,100.79504999999995,115.28254150000002,101.47469999999998C98.49518150000003,103.02607999999998,93.07571350000002,160.60424999999998,89.71587850000003,166.02668"/></path></svg></p> <p>Not even those funky effects we know from Flash back then, which is one of the main advantages of Rapha&euml;lJS: The algorithm interpolating between two paths might lead to quirky results, but is nonetheless bloody good!</p> <pre class="codepen" data-height="300" data-type="result" data-href="ucDwz" data-user="ddprrt" data-safe="true"><code></code><a href="http://codepen.io/ddprrt/pen/ucDwz">Check out this Pen!</a></pre> <script src="http://codepen.io/assets/embed/ei.js"></script> <p>Anyhow, I did want to stick to SMIL, but even by using Tavmjongs data I wasn't able to recreate one transition between two bats. It took me some time to realize how Tavmjong was implementing his animation. Mostly because I didn't take a good look at the values. The <code class="language-markup">&lt;animate&gt;</code>-element is pretty straightforward, but the values do need some explanation: To create an animation from path A to B, the values inside the element have to feature both paths, separated by a semicolon. So if you want a transition from Figure A to B, you first have to include the path in your <code class="language-markup">&lt;path&gt;</code>-element, and then again as the first value tuple in your animation:</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="c">&lt;!-- The 'd' in path is the first bat --&gt;</span> <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"M 256,213 C 245,181 206,187 ..."</span> <span class="na">fill=</span><span class="s">"#000000"</span><span class="nt">&gt;</span> <span class="c">&lt;!-- The 'values' include the first as well as the second bat --&gt;</span> <span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">repeatCount=</span><span class="s">"indefinite"</span> <span class="na">attributeName=</span><span class="s">"d"</span> <span class="na">values=</span><span class="s">"M 256,213 C 245,181 206,187 ... Z; M 212,220 C 197,171 156,153 ... Z;"</span><span class="nt">/&gt;</span> <span class="nt">&lt;/path&gt;</span></code></pre></figure> <h3>Result</h3> <p class="img-holder"><svg id="svgex1" class="responsive-svg" width="600" height="300"><path d="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z" fill="#000000"><animate dur="2s" repeatCount="indefinite" attributeName="d" values="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z;M 212,220 C 197,171 156,153 123,221 109,157 120,109 159,63.6 190,114 234,115 254,89.8 260,82.3 268,69.6 270,60.3 273,66.5 275,71.6 280,75.6 286,79.5 294,79.8 300,79.8 306,79.8 314,79.5 320,75.6 325,71.6 327,66.5 330,60.3 332,69.6 340,82.3 346,89.8 366,115 410,114 441,63.6 480,109 491,157 477,221 444,153 403,171 388,220 366,188 316,200 300,248 284,200 234,188 212,220 Z;"/></path></svg></p> <p>Actually, I lied a little bit. You don't need to include the path inside the "d" attribute of the <code class="language-markup">&lt;path&gt;</code>-element. The animation will work fine even without it. But: if you include the path data directly you can do some more, event-based stuff with your SVG. But more on that later. First, check on some of the attributes of the <code class="language-markup">&lt;animate&gt;</code> element.</p> <h2>Parameters</h2> <p>Some parameters are already visible in the example above: </p> <ul> <li><code class="language-markup">dur</code> is short for "duration" and defines exactly that. Use any value in seconds for that.</li> <li>Similar, <code class="language-markup">repeatCount</code> allows us to define how often the animation is going to be repeated. It takes any number, or <code class="language-markup">indefinite</code> for endless repeat.</li> <li><code class="language-markup">attributeName</code> defines which attribute of the original element is going to be animated. Since the <code class="language-markup">&lt;animate&gt;</code> tag can be used on a multitude of SVG elements, this parameter has a multitude of possibilities. We use the "d" (<em>very</em> short for "path data"), since this is the parameter of the original <code class="language-markup">&lt;path&gt;</code> element.</li> <li>Last, but not least we defined a set of <code class="language-markup">values</code></li> </ul> <p>We are not done with that. One thing you might realize is that the animation always jumps back to it's initial frame (which is why we also need to define the original path in the parent <code class="language-markup">&lt;path&gt;</code> element). To make sure that the ending state is preserved, we add another attribute called <code class="language-markup">fill</code> and set its value to <code class="language-markup">freeze</code>. In other elements, <code class="language-markup">fill</code> is used to define the filling color, in animation it's the state at the end.</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">fill=</span><span class="s">"freeze"</span> <span class="na">repeatCount=</span><span class="s">"1"</span> <span class="na">attributeName=</span><span class="s">"d"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">/&gt;</span></code></pre></figure> <h3>Result</h3> <p>Trigger the animation by clicking or tapping on it.</p> <p class="img-holder" id="ex3"> <svg width="600" class="responsive-svg" height="300"><path id="pathex3" d="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z" fill="#000000"><animate id="animationex3" dur="2s" fill="freeze" begin="click" repeatCount="1" attributeName="d" values="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z;M 212,220 C 197,171 156,153 123,221 109,157 120,109 159,63.6 190,114 234,115 254,89.8 260,82.3 268,69.6 270,60.3 273,66.5 275,71.6 280,75.6 286,79.5 294,79.8 300,79.8 306,79.8 314,79.5 320,75.6 325,71.6 327,66.5 330,60.3 332,69.6 340,82.3 346,89.8 366,115 410,114 441,63.6 480,109 491,157 477,221 444,153 403,171 388,220 366,188 316,200 300,248 284,200 234,188 212,220 Z;"/></path></svg</p> <h2>Triggers</h2> </p>As you've seen, animations can be triggered on certain actions. Use the <code class="language-markup">begin</code> attribute to define the interaction or property which starts the animation, as well as <code class="language-markup">end</code> to define the interaction which should stop it.</p> </p>And this is where this stuff becomes really good, as you can add at least some control to your animation. You can either use DOM events for that, like <code class="language-javascript">click</code> (as shown in the example above) or <code class="language-javascript">mouseover</code>, but you also can use time constraints to apply a certain delay:</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="c">&lt;!-- Triggers the animation after 1s --&gt;</span> <span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">repeatCount=</span><span class="s">"indefinite"</span> <span class="na">attributeName=</span><span class="s">"d"</span> <span class="na">begin=</span><span class="s">"1s"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">/&gt;</span> <span class="c">&lt;!-- Triggers the animation when clicking on the element --&gt;</span> <span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">repeatCount=</span><span class="s">"indefinite"</span> <span class="na">attributeName=</span><span class="s">"d"</span> <span class="na">begin=</span><span class="s">"click"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">/&gt;</span> <span class="c">&lt;!-- Triggers the animation on mouseover, stops it on mouseout --&gt;</span> <span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">repeatCount=</span><span class="s">"indefinite"</span> <span class="na">attributeName=</span><span class="s">"d"</span> <span class="na">begin=</span><span class="s">"mouseover"</span> <span class="na">end=</span><span class="s">"mouseout"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">/&gt;</span> <span class="c">&lt;!-- Triggers the animation on click, stops it also on click --&gt;</span> <span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">repeatCount=</span><span class="s">"indefinite"</span> <span class="na">attributeName=</span><span class="s">"d"</span> <span class="na">begin=</span><span class="s">"click"</span> <span class="na">end=</span><span class="s">"click"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">/&gt;</span></code></pre></figure> <p>These parameters take almost any input based DOM event, but with one very special constraint: The SVG data has to be embedded in the DOM. If you have your SVG in a file and are referencing it in an image tag or whatever, the DOM events won't trigger.</p> <p>Trigger events can be expanded further by not only using the DOM event of an element itself, but also by referencing to an event by another element. For instance, <code class="language-markup">begin="button.click"</code> allows us to trigger the animation once a certain element with the id of <code class="language-markup">button</code> has been clicked.</p> <p>This gives us a multitude of possibilities. Look at that the following example:</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;svg&gt;</span> <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span><span class="nt">&gt;</span> <span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">fill=</span><span class="s">"freeze"</span> <span class="na">begin=</span><span class="s">"click"</span> <span class="na">id=</span><span class="s">"anim1"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">/&gt;</span> <span class="nt">&lt;/path&gt;</span> <span class="nt">&lt;/svg&gt;</span> <span class="nt">&lt;svg&gt;</span> <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span><span class="nt">&gt;</span> <span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">fill=</span><span class="s">"freeze"</span> <span class="na">begin=</span><span class="s">"anim1.begin"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">/&gt;</span> <span class="nt">&lt;/path&gt;</span> <span class="nt">&lt;/svg&gt;</span></code></pre></figure> <p>Here we start the second animation once the first one has already started.</p> <h3>Result</h3> <p>Click on the left bat to see the magic happen.</p> <p class="img-holder"> <svg width="300" height="200" class="responsive-svg" ><path d="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z" fill="#000000" transform="scale(0.5)"><animate id="animex7" dur="2s" fill="freeze" begin="click" repeatCount="1" attributeName="d" values="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z;M 212,220 C 197,171 156,153 123,221 109,157 120,109 159,63.6 190,114 234,115 254,89.8 260,82.3 268,69.6 270,60.3 273,66.5 275,71.6 280,75.6 286,79.5 294,79.8 300,79.8 306,79.8 314,79.5 320,75.6 325,71.6 327,66.5 330,60.3 332,69.6 340,82.3 346,89.8 366,115 410,114 441,63.6 480,109 491,157 477,221 444,153 403,171 388,220 366,188 316,200 300,248 284,200 234,188 212,220 Z;"/></path></svg><svg width="300" height="200"><path d="M 166,154 C 179,119 154,95.4 114,79.3 155,79.1 197,78.9 239,78.7 242,103 250,109 283,109 289,109 290,93.9 291,83.7 292,88.3 292,92.9 293,97.5 295,97.5 298,97.5 300,97.5 302,97.5 305,97.5 307,97.5 308,92.9 308,88.3 309,83.7 310,93.9 311,109 317,109 350,109 358,103 361,78.7 403,78.9 445,79.1 486,79.3 446,95.4 421,119 434,154 377,151 320,151 300,207 280,151 223,151 166,154 Z" transform="scale(0.5)"><animate id="animation2" dur="2s" fill="freeze" begin="animex7.begin" repeatCount="1" attributeName="d" values="M 166,154 C 179,119 154,95.4 114,79.3 155,79.1 197,78.9 239,78.7 242,103 250,109 283,109 289,109 290,93.9 291,83.7 292,88.3 292,92.9 293,97.5 295,97.5 298,97.5 300,97.5 302,97.5 305,97.5 307,97.5 308,92.9 308,88.3 309,83.7 310,93.9 311,109 317,109 350,109 358,103 361,78.7 403,78.9 445,79.1 486,79.3 446,95.4 421,119 434,154 377,151 320,151 300,207 280,151 223,151 166,154 Z; M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z;"/></path></svg> </p> <h2>Events</h2> <p id="feature-test1" class="sorrynote note">Sorry, your browser does not support SMIL events<script>el=document.getElementById('feature-test1');function tstdoit() {el.className="note"};el.innerHTML += '\n<' + 'svg width="0" height="0"' + '>' +'<' + 'path' + ' d="m 1,1 1,1 0,0"><animate values="m 0,0 0,0 0,0" attributeName="d" begin="0s" repeatCount="0" onbegin="tstdoit()"/></path></svg>';</script></p> <p>SMIL supports some events to add additional control with JavaScript to your animation needs. Unfortunately, at the moment animation events are just implemented by Firefox (and pre-Blink Opera ...). I wanted to use this method not only to show some elements once the animation is done, but also to keep the state ready for the next animation, by removing the <code class="language-markup">&lt;animate&gt;</code>-element and changing the original path.</p> <p>I dropped this idea due to browser constraints, tough it would've been easy: Simply add the callback into your markup, or use <code class="language-javascript">addEventListener</code> to achieve the same.</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;animate</span> <span class="na">dur=</span><span class="s">"2s"</span> <span class="na">fill=</span><span class="s">"freeze"</span> <span class="na">begin=</span><span class="s">"click"</span> <span class="na">repeatCount=</span><span class="s">"1"</span> <span class="na">attributeName=</span><span class="s">"d"</span> <span class="na">onend=</span><span class="s">"cry()"</span> <span class="na">values=</span><span class="s">"..."</span> <span class="nt">&gt;</span></code></pre></figure> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">cry</span><span class="p">()</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="s2">"I'M BATMAN"</span><span class="p">);</span> <span class="p">}</span></code></pre></figure> <p>Other events include <code class="language-javascript">onbegin</code> which is obviously triggered when the animation starts, and <code class="language-javascript">onrepeat</code>, which counts the number of interations the animation has run and fires every time one iteration is complete.</p> <h3>Result</h3> <p>Start the animation by clicking or tapping the bat!</p> <p class="img-holder" id="ex5"> <svg id="svg1" width="600" height="300" class="responsive-svg" ><script>function cry() {alert("I'M BATMAN");}</script><path id="pathex5" d="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z" fill="#000000"><animate id="animationex5" dur="2s" fill="freeze" begin="click" onend="cry()" repeatCount="1" attributeName="d" values="M 256,213 C 245,181 206,187 234,262 147,181 169,71.2 233,18 220,56 235,81 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 310,62.7 311,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 367,18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 300,284 291,184 272,185 256,213 Z;M 212,220 C 197,171 156,153 123,221 109,157 120,109 159,63.6 190,114 234,115 254,89.8 260,82.3 268,69.6 270,60.3 273,66.5 275,71.6 280,75.6 286,79.5 294,79.8 300,79.8 306,79.8 314,79.5 320,75.6 325,71.6 327,66.5 330,60.3 332,69.6 340,82.3 346,89.8 366,115 410,114 441,63.6 480,109 491,157 477,221 444,153 403,171 388,220 366,188 316,200 300,248 284,200 234,188 212,220 Z;"/></path></svg</p> <p>Again, this will work only if the SVG is included directly in your DOM.</p> <h3>Feature test</h3> <p>As you all know, we just should feature detect to check if we're able to use SMIL. However, it might be that you get a false positive in IE9, according to this (somewhat old) <a href="https://github.com/Modernizr/Modernizr/issues/356">Modernizr issue</a>, so be aware!</p> <p>With that one you can detect SMIL:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">return</span> <span class="o">!!</span><span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'animate'</span><span class="p">);</span></code></pre></figure> <p>Place it in a try-catch block! To check for event callbacks, this is how it <em>should</em> work.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">el</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'animate'</span><span class="p">);</span> <span class="k">return</span> <span class="o">!!</span><span class="nx">el</span><span class="p">.</span><span class="nx">onend</span><span class="p">;</span></code></pre></figure> <p>However, not even Firefox provides interfaces in their DOM API. This is a workaround: Add an almost empty SVG markup to your DOM and have it call a function once it starts. In this function, set your bools or classes, or whatever you like or need.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">smilEventsSupported</span><span class="p">()</span> <span class="p">{</span> <span class="c1">//set classes or whatever </span> <span class="p">}</span> <span class="c1">//can be any element </span> <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">+=</span> <span class="s1">'&lt;svg width="0" height="0"&gt;'</span> <span class="o">+</span> <span class="s1">'&lt;path d="m 1,1 1,1 0,0"&gt;'</span> <span class="o">+</span> <span class="s1">'&lt;animate values="m 0,0 0,0 0,0"'</span> <span class="o">+</span> <span class="s1">'attributeName="d" begin="0s" repeatCount="0"'</span> <span class="o">+</span> <span class="s1">'onbegin="smilEventsSupported()"/&gt;'</span> <span class="o">+</span> <span class="s1">'&lt;/path&gt;&lt;/svg&gt;'</span></code></pre></figure> <h2>Bottom line</h2> <p>This blog entry is based on about a weekend of research on that topic, fooling and playing around with values and constantly checking the <a href="http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#SMIL_animations">specification</a>. And I'm sure that I just scratched the surface! I stumpled upon parameters such as <code class="language-markup">keytimes</code>, <code class="language-markup">keyspines</code> or <code class="language-markup">calcMode</code>, which I didn't read in depth. Also the possibility of an <code class="language-markup">animationPath</code> is available, where I've no bloody clue how they can be created whatsoever. SVG animations are a beast of their own, and very powerful once you understand them.</p> <p>Use is limited, tough. Microsoft has no plans of including SMIL in their current versions of IE. The spec has been out there for a while, but even the preview version of IE11 shows no sign of implementation. Furthermore, if you want to use that stuff on mobile browsers, be aware that performance might be below your expectations. Even iOS Safari, which is still one of the most performant mobile browsers out there, has a clear performance issue when it comes to basic tweening. It's not as bad as animating with Rapha&euml;lJS, tough, because there you just won't get any animation at all.</p> Preparing for an unprefixed future 2013-07-02T00:00:00+00:00 http://www.fettblog.eu/blog/2013/07/02/preparing-for-an-unprefixed-future <p>I realized recently that I don't have to use "<em>-webkit</em>" on the transition property anymore for Chrome. Actually, to use transitions in modern desktop browsers I don't have to use any prefix at all. This was almost unimaginable a few months ago!</p> <p>This is all part of a certain change. Google, Microsoft and Mozilla have a clear direction on that topic: If you are going to use the stable version of the browser, features don't require a vendor prefix. Actually, this is something we are already used to in the desktop world to a certain extent: Nobody would use a vendor prefix on <em>border-radius</em> or <em>box-shadow</em> anymore, because they left the experimental state.</p> <!--more--> <h2>The Past</h2> <p>Vendor prefixed features were really good back then, when we needed to overcome the "legacy browser" mindset of developers and clients alike, and had to show what was possible with all the new features now available. This trend was also driven by the new possibilities of HTML5 apps on mobile browsers: Everything you can do natively with CSS3 helps download and render times on your mobile client.</p> <p>Those features matured and became stable, and are now base of our day to day work. We have a new base available with browser updating on a regular basis and ensuring that features become available in a shorter amount of time.</p> <h2>... and now</h2> <p>Experimental (now) means: really not ready for production code. Experimental features are for you developers, to try and get familiar with the new APIs. Experimental (and thus prefixed) features are not for the things you throw at your users. Those features will most likely fail, or behave differently in other browsers, or worse: will change their API over the course of time. So don't use them in your production code. They are not ready yet.</p> <p>This goes for all those legacy browsers out there we still have to support. Developers and browser vendors created awareness that it's okay not to have the same experience in legacy IE compared to modern browsers. Everybody's fine with that. In the meantime, Android Stock Browser has become the biggest problem for web developers. Android browser (amongst others) has one really big issue: It has plenty of new features available, but they really suck in their implementation. Now-common things like <em>box-shadow</em>, <em>transitions</em> and even <em>border-radius</em> not only behave differently, but also can cause performance and display issues. Keep in mind that "available" does not have to mean "usable".</p> <p>So it's time to treat those features for what they are: experimental. Not ready for production code. Drop those vendor-prefixes!</p> <p>We don't care anymore if our corners are rounded on legacy desktop browsers, and we shouldn't care either on legacy mobile browsers.</p> <h2>but but but ...</h2> <p>Yeah, I know: This means a big change on certain platforms! While a lot of features are already unprefixed available, others are still in the experimental stage on almost all mobile browsers. Even such "game-changers" like <em>linear-gradient</em> or <em>animation</em>, and even on upcoming platforms. This goes especially for Mobile Safari, who -- according to the preview version -- still has some features prefixed. But hey, there is (or at least might be) a reason for that! So treat them for what they are: not ready yet.</p> <h2>Prefix free!</h2> <p>"Prefix-free" should not only be a JavaScript library, but a new mindest. And yeah: I do want a Modernizr distribution, that just checks for unprefixed and stable features.</p> Caring for Sharing: Social share URLs 2013-06-17T00:00:00+00:00 http://www.fettblog.eu/blog/2013/06/17/caring-for-sharing-social-share-urls <p>I truly hate social media share buttons. Especially those plugins which not only are coded badly but also create heavy traffic and performance issues. Plus, they're a pain in the ass to place correctly. If you really want to use (or have to use) those social liking/sharing/tweeting stuff, use their share URLs. Every single one of them has such, and you can either open them in a pop-up or in a new window. Here they are:</p> <!--more--> <h3>Twitter</h3> <figure class="highlight"><pre><code class="language-html" data-lang="html">http://twitter.com/home?status=Check%20out%20{articleUrl}</code></pre></figure> <h3>Facebook</h3> <figure class="highlight"><pre><code class="language-html" data-lang="html">http://www.facebook.com/sharer.php?u={articleUrl}</code></pre></figure> <h3>LinkedIn</h3> <figure class="highlight"><pre><code class="language-html" data-lang="html">http://www.linkedin.com/shareArticle?mini=true<span class="err">&amp;</span>url={articleUrl} <span class="err">&amp;</span>title={articleTitle} <span class="err">&amp;</span>summary={articleSummary} <span class="err">&amp;</span>source={articleSource}</code></pre></figure> <h3>Google+</h3> <figure class="highlight"><pre><code class="language-html" data-lang="html">https://plus.google.com/share?url={articleUrl}</code></pre></figure> <h3>Pinterest</h3> <figure class="highlight"><pre><code class="language-html" data-lang="html">http://pinterest.com/pin/create/button/?url={articleUrl} <span class="err">&amp;</span>media={articleImage}</code></pre></figure> <p>Just replace {articleUrl} with the respective location.</p> Preserving aspect ratio for embedded iframes 2013-06-16T00:00:00+00:00 http://www.fettblog.eu/blog/2013/06/16/preserving-aspect-ratio-for-embedded-iframes <p>If you want to use videos on your webpage which are hosted on another server (YouTube, Vimeo, whatever), you most likely will use their embedding possibility rather than the HTML5 &lt;video&gt; Tag or a flash plugin hosted on your server. These embedding codes mostly use &lt;iframe&gt;, which is good since they detect all your needs on their site, like in "what format do you need", "use either HTML5 or Flash", or "streaming HD or lower definition for mobile phones". A lot of decisions are taken from you!</p> <p>However, you will have to specify the size of the iframe. And if your responsive layout scales your content flexibly according to the viewport width rather than fixed widths on breakpoints (like this page), the embedding iframe may look especially bad on smaller screens. Here's a solution to keep the aspect ratio:</p> <!--more--> <p>I'll use a trailer video from on of our projects. The girl in this video is Tina, one of our developers. The standard embedding code I get from vimeo looks something like this:</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;iframe</span> <span class="na">src=</span><span class="s">"http://player.vimeo.com/video/61337126"</span> <span class="na">width=</span><span class="s">"550"</span> <span class="na">height=</span><span class="s">"281"</span> <span class="na">frameborder=</span><span class="s">"0"</span><span class="nt">&gt;</span> <span class="nt">&lt;iframe&gt;</span></code></pre></figure> <p>Alright, the cheapest way to achieve resizing according to viewport/content width is by simply adding some CSS for your iframe:</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nt">iframe</span> <span class="p">{</span> <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>And the iframe is as wide as the container you put him in. However, the height can't be controlled that easily. To preserve that aspect ratio, you need a wrapper div, let's call it .aspect-ratio:</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.aspect-ratio</span> <span class="p">{</span> <span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">padding-bottom</span><span class="p">:</span> <span class="m">51%</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>This div is as wide as the content/viewport area, the height is zero. So practically this shouldn't be visible. However. The padding of this one is the height of the video divided through its width, or in other words: the aspect ratio in percent! No matter how much you resize your window, it will always stay in this ratio. So, all you need now is to let the iframe inside flow to each corner of its parent:</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.aspect-ratio</span> <span class="nt">iframe</span> <span class="p">{</span> <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span> <span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>Since the parent element has no height, we place it absolutely on the top left corner and let the iframe use the space created by the padding.</p> <p>And voilá:</p> <div class="content-wrapper"> <div class="aspect-video"><iframe src="http://player.vimeo.com/video/61337126" height="281" width="550" allowfullscreen="" frameborder="0"></iframe></div> </div> <p>Resize the window and try it!</p> beyond our own noses - the "beyond tellerrand 2013" 2013-06-06T00:00:00+00:00 http://www.fettblog.eu/blog/2013/06/06/beyond-our-own-noses-the-beyond-tellerrand-2013 <p>Not one single line of JavaScript code? At a conference where web developers visibly outnumbered the rest of the audience? Yes, they can: D&uuml;sseldorf's <a href="http://2013.beyondtellerrand.com">"beyond tellerrand"</a> conference saw its third edition this year, and what initially started as a somewhat obscure insider gathering is now a definite "Be there or be square" for designers and coders. How come?  Well, "beyond tellerrand" translates as "beyond our own noses", and that is exactly what the event is about: Widening the horizon of web developers. Planting fresh ideas into bright brains. Seeding energizing motivation into otherwise boring "business as usual". Throwing a spotlight on fields which are closely linked to our own work, yet rarely part of our considerations. In other words, the principles of the Netural environment get blown up to a full-size conference here.</p> <!--more--> <p class="img-holder"><a href="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond1.jpg"><img src="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond1-300x200.jpg" alt="beyond1" width="300" height="200" class="alignnone size-medium wp-image-209" /></a></p> <p><a href="http://adactio.com">Jeremy Keith</a>'s opening keynote set the pace and defined the discourse level. He discussed the web's sustainability and longevity, stating that "the web never forgets" is not necessarily set in stone - at least not as solidly as some would like to believe. He challenged the audience to name web addresses which are still relevant after 15 years of existence.  Indeed, there are not many, if any. Keith then presented the archivars of the web (the <a href="http://archive.org/web/web.php">Wayback Machine</a>) and introduced a number of other relevant projects in this field.</p> <p>Regarding design, the loudest slogan at the conference was "Photoshop Lies!". In the face of the multiple aspects of mobile design, Adobe's long-familiar tool runs the danger of being reduced to a sidekick role in design processes, still indispensable for final touch ups,  but replaced by early prototyping in HTML and CSS  for the new flexible layouts. This view was stated by <a href="https://twitter.com/owltastic">Megan Fisher </a>and shared by Twitter's <a href="http://jbrewer.me">Josh Brewer</a>, who took a radical, if time-honored approach to getting the message across: Grabbing his six-string and singing his "Photoshop, you damn liar!" protest song, he gave a whole new meaning to the word "keynote". </p> <p class="img-holder"><a href="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond3josh.jpg"><img src="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond3josh-300x200.jpg" alt="beyond3josh" width="300" height="200" class="alignnone size-medium wp-image-211" /></a></p> <p>Oh, yes - CSS: Of course there were also stylesheet gurus, presenting new principles and methods to update processes on the developers' side. The CSS Wizard himself, <a href="http://csswizardry.com">Harry Roberts</a>, gave vivid examples for making directives scaleable and recyclable, and <a href="http://bradfrostweb.com">Brad Fros</a>t with his Pattern Lab proclaimed the birth of the "Atomic Design" movement: not just a buzzword (although that would have been helfpful enough in itself), rather a well-considered new structuring of websites.</p> <p>My personal highlight at the "beyond tellerrand" shone a good distance away from development, design and typo themes: <a href="http://www.forbes.com/fdc/welcome_mjx.shtml">Kate Kiefer-Lee </a>discussed the voice in which a website talks to the user. As the content mastermind of newsletter providers <a href="http://mailchimp.com">Mailchimp</a>, she discussed the thin red line between humor and impertinence and the art of getting the message across according to varying circumstances. A visible result of her work is <a href="http://voiceandtone.com">Voice and Tone</a>, a Website which does not only observe user moods, but also works on them.</p> <p class="img-holder"><a href="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond2kate.jpg"><img src="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond2kate-300x200.jpg" alt="beyond2kate" width="300" height="200" class="alignnone size-medium wp-image-210" /></a></p> <p>Living up to its name, beyond tellerrand presents not only elite exponents of the web, there are surprise cameos as well: Enter <a href="http://www.jamesvictore.com">James Victore</a>, whose polemical "500 years America" posters produced disputes in the 90ies and are now on exhibition in the MOM. Working not just beyond the rim of our plates, but at least a cupboard away, he still brought a message that gave the audience pause: Do a job you yourself can enjoy, because your work is a gift!</p> <p class="img-holder"><a href="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond4james.jpg"><img src="http://www.fettblog.eu/wp-content/uploads/2013/06/beyond4james-300x200.jpg" alt="beyond4james" width="300" height="200" class="alignnone size-medium wp-image-212" /></a></p> <p>So this was my first "beyond tellerrand", and it was definitely not my last one. The phantastic lineup of choice international speakers and the flawless organisation created a unique environment. Somehow the conference resembled a class reunion of a web developer scene which enjoyed a time out from JavaScript. Kudos to organiserr Marc Thiele: Marc, you have created something absolutely beautiful!</p> <p>Fotos by <em><a href="https://twitter.com/stn1978" target="_blank">Stefan Nitzsche</a></em></p> Remake. Remodel. Part 2 2013-04-30T00:00:00+00:00 http://www.fettblog.eu/blog/2013/04/30/remake-remodel-part-2 <p><a href="http://blog.cloudfour.com">@grigs</a> said I should blog, so I'm going to take this thing seriously now. I spent the last few days (finally) creating a new look for my website. With the advent of flat designs I think I'm able to create at least one style that doesn't fail completely. And actually I'm pretty happy with it. </p> Nobody wants HTML5 apps 2013-04-24T00:00:00+00:00 http://www.fettblog.eu/blog/2013/04/24/nobody-wants-html5-apps <p>There's much buzz going on about HTML5 being just the wrong way of developing apps. Facebook switched to "kind of native" a while ago (and still has an app below standards), now <a href="http://venturebeat.com/2013/04/17/linkedin-mobile-web-breakup/" target="_blank">LinkedIn</a> dropped their HTML5 based app in favor of a native one.</p> <p>WebApps or HTML5 apps don't get much love, despite a lot of people hating to get almost forced to use native apps instead of a <a href="http://www.netmagazine.com/news/devs-rally-against-mobile-web-doorslams-132705" target="_blank">browser counterpart</a>.</p> <!--more--> <blockquote> <p>I'm in the browser, of course I want to view the web version!</p> </blockquote> <p>That's true! From the ca. 40 apps I've installed on my device, I use 4-8 regularly. Most used app next to twitter is my web browser, and there I like direct access to my services. One of the main reasons companies give once they repel HTML5 is the often heard: "The technology isn't ready yet!".</p> <p>Some examples to counter that point:</p> <h2>Forecast.io</h2> <p>I love weather reports. It's a shtick, I guess. I learned about <a href="http://forecast.io">forecast.io</a> and was pretty amazed that the website didn't want to lead me to the app store, but instead just bookmark their site on my home screen. Said and done, I started the app the first time and fell in love with the UI.</p> <p class="img-holder"> <a href="http://www.fettblog.eu/wp-content/uploads/2013/04/Foto-24.04.13-20-53-46.png"><img src="http://www.fettblog.eu/wp-content/uploads/2013/04/Foto-24.04.13-20-53-46-200x300.png" alt="Forecast.io" width="200" height="300" class="alignnone size-medium wp-image-124" /></a> </p> <p class="img-holder"> <a href="http://www.fettblog.eu/wp-content/uploads/2013/04/Foto-24.04.13-20-52-57.png"><img src="http://www.fettblog.eu/wp-content/uploads/2013/04/Foto-24.04.13-20-52-57-200x300.png" alt="Forecast.io" width="200" height="300" class="alignnone size-medium wp-image-123" /></a> </p></p> <p>Be sure to check out the video by <a href="http://www.youtube.com/watch?v=ystXvkb0yIY" target="_blank">CNET</a> so you get a glimpse of how this neat little application feels. All done with HTML5, and by just adding it to the home screen, you get the full Safari features and not just the stripped down UIWebView.</p> <p>Be sure to <a href="http://blog.forecast.io/its-not-a-web-app-its-an-app-you-install-from-the-web/">read their blog post</a> on why and how they created their application using HTML5. I just wonder why I can't use it in my browser, be sure to include that option in the future!</p> <h1>ExFM</h1> <p>Next is ExtFM. ExtFM is a music discovery application, where you can find new tunes according to your listening habits. Good for music nerds like me, and even better for HTML5 enthusiasts. Their application was written using PhoneGap. You know, that tool that allows you to use web rendering for your applications. And again, a very nice UI with good responses and virtually not distinguishable from a native app.</p> <p class="img-holder"> <a href="http://www.fettblog.eu/wp-content/uploads/2013/04/Foto-24.04.13-20-54-34.png"><img src="http://www.fettblog.eu/wp-content/uploads/2013/04/Foto-24.04.13-20-54-34-200x300.png" alt="ExtFM, based on PhoneGap" width="200" height="300" class="alignnone size-medium wp-image-125" /></a> </p> <p>Even cooler is that they switched <a href="http://phonegap.com/blog/2013/04/23/story-behind-exfm/" target="_blank">from native to HTML5</a>, and not the other way round. "We have now more stars" - quotes aside they show some useful insights in how they developed the new client.</p> <h2>So where's the problem?</h2> <blockquote> <p>The problem isn't that HTML5 isn't ready, it’s that the ecosystem doesn’t support it!</p> </blockquote> <p>That's one of the true quotes in Kirin Prasads interview. Why so? The examples above showed some great iOS apps. iOS Safari -- while not being a perfect browser -- is actually quite decent and performs really good. Apps based on iOS Safari aren't the culprit in this discussion.</p> <p>It's mostly Android. HTML5/PhoneGap apps use android.webkit.Webview, and Android WebView/Android Stock browser simply sucks. Everybody wants Android browser to silently die and be replaced with another web engine that actually can perform and provide us with all our HTML5 needs.</p> <p>Developing for WebView is a challenge. ExFM, while providing a great User Experience with a PhoneGap app on iOS, still have their Android app native, even though they plan to change that.</p> <blockquote class="twitter-tweet" lang="de"> <p> @<a href="https://twitter.com/ddprrt">ddprrt</a> Thank you! We're working on Android now, but WebView is definitely a challenge. </p>— exfm (@exfm) <a href="https://twitter.com/exfm/status/327133594243960834">24. April 2013</a> </blockquote> <p>I think history is repeating itself. In the old desktop days there was a slight transition from using installed software to just starting the browser. I switched from endless installs (and tedious updates) of mail software to Google Mail in 2006 and don't regret one day since then. I don't install office software anymore because I can use Google Docs, and if there would be a nice Spotify webapp, I'm sure I would use that instead of the ever-crashing native client.</p> <p>It wasn't always like that, so I think it just will take some time people will do the same transition in the mobile world. Browsers need to supply certain features and we're good!</p> <p>After all, installing and updating my apps annoyed me fairly early in my mobile life.</p> <h2>Further reading:</h2> <ul> <li><a href="http://venturebeat.com/2013/04/17/linkedin-mobile-web-breakup/" target="_blank">LinkedIn drops HTML5 app</a></li> <li><a href="http://www.netmagazine.com/news/devs-rally-against-mobile-web-doorslams-132705" target="_blank">Devs rally against mobile web doorslams</a></li> <li><a href="http://phonegap.com/blog/2013/04/23/story-behind-exfm/" target="_blank">The story behind EXFM</a></li> <li><a href="http://blog.forecast.io/its-not-a-web-app-its-an-app-you-install-from-the-web/">It's not a web app, it's an app you install from the web -- forecast.io</a></li> </ul> "Tech­no­logie­plausch­erl" at Netural 2012-11-26T00:00:00+00:00 http://www.fettblog.eu/blog/2012/11/26/technologieplauscherl-at-netural <p>On Thursday we held the "<a href="http://www.barcamp.at/Technologieplauscherl" target="_blank">Technologieplauscherl</a>" at Netural for the first time. The "Plauscherl" (which translates to technology talk, but is unrelated to my beloved F.E.T.T.) is some sort of short evening barcamp held by the local dev community of Linz in different locations, mostly offices from attending persons. In its eight edition it had the unique topic "books", which was also a first for the group, I guess. The goal was: Present a book and give a short review.</p> Preventing FOUT in IE9 2012-11-16T00:00:00+00:00 http://www.fettblog.eu/blog/2012/11/16/preventing-fout-on-ie9 <p>FOUT is an abbrevation for flash of unstyled text (or type) and is one of those really nasty bits in modern frontend development. Summarized it means that if you use webfonts it might happen that you first see your text displayed in a fallback font until the downloadble webfont is loaded, parsed and inserted. <a href="http://remysharp.com/2009/06/23/safaris-problem-with-font-face/" target="_blank">Remy Sharp</a> and <a href="http://paulirish.com/2009/fighting-the-font-face-fout/" target="_blank">Paul Irish</a> did a lot of research on that topic more than three years ago.</p> <p>Luckily, with today's browsers you won't be seeing that so often as you might have been used to. The Webkit browsers as well as Firefox are really good in handling Webfonts, and even Internet Explorer, now in version 10, focusses heavily on webfont integration. However, IE10 just came out, and several people -- at least in bigger companies -- are just switching from ancient browsers to IE9...</p> <!--more--> <h2>And IE9 FOUTs. Badly.</h2> <p>In one certain case, after considering server architecture and our clients desktop environment, and even after applying caching routines, this effect was so bad that it wasn't bearable at all. Put there is a certain way of handling this. First of all, we take a look at the currently recommended way of including webfonts for cross browser purposes:</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="k">@font-face</span> <span class="p">{</span> <span class="nl">font-family</span><span class="p">:</span> <span class="s2">'MySpecialFont'</span><span class="p">;</span> <span class="nl">src</span><span class="p">:</span> <span class="sx">url('../fonts/MySpecialFont.eot')</span><span class="p">;</span> <span class="c">/* IE9 compatibility mode */</span> <span class="nl">src</span><span class="p">:</span> <span class="sx">url('../fonts/MySpecialFont.eot?#iefix')</span> <span class="n">format</span><span class="p">(</span><span class="s2">'embedded-opentype'</span><span class="p">),</span> <span class="c">/* &lt; IE9 */</span> <span class="sx">url('../fonts/MySpecialFont.woff')</span> <span class="n">format</span><span class="p">(</span><span class="s2">'woff'</span><span class="p">),</span> <span class="c">/* Modern browers */</span> <span class="sx">url('../fonts/MySpecialFont.ttf')</span> <span class="n">format</span><span class="p">(</span><span class="s2">'truetype'</span><span class="p">),</span> <span class="c">/* iOS, Android, Safari */</span> <span class="sx">url('../fonts/MySpecialFont.svg#MySpecialFont')</span> <span class="n">format</span><span class="p">(</span><span class="s2">'svg'</span><span class="p">);</span> <span class="c">/* Legacy iOS */</span> <span class="nl">font-weight</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>All modern browsers are capable of handling WOFF, so this should be your format of choice. EOT is more or less just for legacy purposes, and so is TrueType or SVG. So, if you are just reducing your Webfont declaration to the one you need for modern browsers, you end up with this.</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="k">@font-face</span> <span class="p">{</span> <span class="nl">font-family</span><span class="p">:</span> <span class="s2">'MySpecialFont'</span><span class="p">;</span> <span class="nl">src</span><span class="p">:</span> <span class="sx">url('../fonts/MySpecialFont.woff')</span><span class="p">;</span> <span class="nl">font-weight</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>Maybe you add a fallback for older mobile devices, which know how to handle the comma seperated syntax.</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="k">@font-face</span> <span class="p">{</span> <span class="nl">font-family</span><span class="p">:</span> <span class="s2">'MySpecialFont'</span><span class="p">;</span> <span class="nl">src</span><span class="p">:</span> <span class="sx">url('../fonts/MySpecialFont.woff')</span><span class="p">,</span> <span class="sx">url('../fonts/MySpecialFont.ttf')</span><span class="p">,</span> <span class="sx">url('../fonts/MySpecialFont.svg')</span><span class="p">;</span> <span class="nl">font-weight</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>Now, the only browser with FOUT would be IE9 by today's standards. IE9 has the problem that he first loads the stylesheet and then loads the font. The time between those two loading processes can vary. However, if you happen to have the font right here after parsing through the whole stylesheet, you won't get a FOUT. We simply can achieve this by embedding the whole font as a data-URI:</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="k">@font-face</span> <span class="p">{</span> <span class="nl">font-family</span><span class="p">:</span> <span class="s2">'MySpecialFont'</span><span class="p">;</span> <span class="nl">src</span><span class="p">:</span> <span class="sx">url("data:application/x-font-woff;base64,MUMBOJUMBO")</span><span class="p">,</span> <span class="sx">url('../fonts/MySpecialFont.ttf')</span><span class="p">,</span> <span class="sx">url('../fonts/MySpecialFont.svg')</span><span class="p">;</span> <span class="nl">font-weight</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>Use a tool <a href="http://t.larskl.de/dataurl" target="_blank">like that one</a> for generating those embedded data streams. Now you need to support IE7 and IE8 extra, because IE9 might load the EOT file before checking on the embedded WOFF. We're doing this by creating another Stylesheet, soley for IE legacy purposes. With CSS preprocessors it's a quick thing to do.</p> <figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="k">@font-face</span> <span class="p">{</span> <span class="nl">font-family</span><span class="p">:</span> <span class="s2">'MySpecialFont'</span><span class="p">;</span> <span class="nl">src</span><span class="p">:</span> <span class="sx">url('../fonts/MySpecialFont.eot')</span><span class="p">;</span> <span class="nl">font-weight</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>Seperate those two files by including them in that way:</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="c">&lt;!--[if lt IE 9]&gt;&lt;link rel="stylesheet" href="../static/css/styleie8.css"&gt;&lt;![endif]--&gt;</span> <span class="c">&lt;!--[if gt IE 8]&gt;&lt;!--&gt;</span><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"../static/css/style.css"</span><span class="nt">&gt;</span> <span class="c">&lt;!--&lt;![endif]--&gt;</span></code></pre></figure> <p>The seperating of those browser related stylesheets might not be the most elegant way, but it definitely works and using CSS preprocessors, you won't have that much work in generating those files.</p> Remake, Remodel. 2012-11-15T00:00:00+00:00 http://www.fettblog.eu/blog/2012/11/15/remake-remodel <p>Let's face it: ModX -- even with the Articles Plugin -- isn't suitable for fast blogging. At least not in the way I need it. So I switched back to Wordpress. Setting the whole thing up from scratch. So there will be some changes in the next days/weeks/months.</p> Forcing browsers to print all pages in grayscale 2012-06-11T00:00:00+00:00 http://www.fettblog.eu/blog/2012/06/11/forcing-chrome-to-print-all-pages-in-grayscale <p><em>Slightly updated on 2014/05/27</em></p> <p>Very short snipplet, but very effective.</p> <p>With Chrome 18 CSS3 filters are finally implemented and you can do amazing stuff with them. One thing we tried and which we found rather useful than fancy is to force Chrome printing all content on your website in grayscale.</p> <!--more--> <p>And this is the code:</p> <pre class="prettyprint language-css"><code class=" language-css">@media print { body { -webkit-filter: grayscale(100%); filter: grayscale(100%); /* future-proof */ } } </code> </pre> <p>And that's it!</p> <h2>Update</h2> <p>And for the record, here is the full flegded version covering all major browsers:</p> <pre class="prettyprint language-css"><code class=" language-css"> @media print { body { /* IE4-8 and 9 (deprecated). Thanks Travis for the tip! */ filter: Gray(); /* SVG version for IE10, Chrome 17, FF3.5, Safari 5.2 and Opera 11.6 -- does not need to be prefixed. See below */ filter: url('#grayscale'); /* CSS3 filter */ -webkit-filter: grayscale(100%); filter: grayscale(100%); /* future-proof */ } } </code> </pre> And here's the SVG Markup for the grayscale filter: <pre class="prettyprint"><code class="language-markup"> &lt;svg xmlns="http://www.w3.org/2000/svg"&gt; &lt;filter id="grayscale"&gt; &lt;feColorMatrix type="matrix" values="0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0"/&gt; &lt;/filter&gt; &lt;/svg&gt;</code> </pre> Of mice and touches 2012-05-01T00:00:00+00:00 http://www.fettblog.eu/blog/2012/05/01/ie9-mobile-is-no-touch-browser <p>Unbelievable, but true: Imagine you bought a new Windows Phone 7 (e.g. Nokia Lumia or Samsung Omnia) with "Mango" on it and try to get your mobile web app running, you will be really surprised when finding out, that there's no touch event available.</p> <p> "But that's what i do", you will say, "I touch my shiny phone all the time!". And you are right with that, nonetheless, IE9 mobile does not register <code>touchstart</code>, <code>touchend</code> or <code>touchmove</code>. Instead of those well known events, IE9 thinks of your finger as a mouse. </p> <!--more--> <p> Usually, when developing for mobile web apps, you will have some code like this in your JS:</p> <pre class="prettyprint language-javascript"><code class=" language-javascript">var clickevent = (ontouchstart in window) ? 'touchstart' : 'click';</code> </pre> <p>And add event listeners using that variable.</p> <pre class="prettyprint language-javascript"><code class=" language-javascript"> elem.addEventListener(clickevent, function(e) { ... }); </code> </pre> <h2>IE9 is no touch browser</h2> <p> That's good for developing your web app on the desktop as well as testing/using it on your mobile device. Even for IE9 Mobile, since all mobile browsers can handle <code>onclick</code>. But once you need gestures, you're going to need mouse events for deskop and IE9 Mobile. </p> <p> Onfortunately, the touch event objects, while not that much different from mouse events in terms of properties, differ in one significant point: There can be more than one touch event at a time. </p> <p>A way of handling both mouse and touch events would be something like this, considering you allow just one touch at a time and don't need complex touch related stuff:</p> <pre class="prettyprint language-javascript"><code class=" language-javascript"> elem.addEventListener('touchstart', function(ev) { myMethod(ev.changedTouches[0]); }); elem.addEventListener('mousedown', myMethod); function myMethod(ev) { //do something with ev.screenX and ev.screenY } </code> </pre> <p>Same for <code>touchend</code> --&gt; <code>mouseup</code> and <code>touchmove</code> --&gt; <code>mousemove</code>.</p> <p>This also means: it is <strong>not possible to have multi-touch gestures in IE9 Mobile</strong></p> <h2>Why does IE9 Mobile behave so different?</h2> <p>There's one simple explanation: Microsoft tried to put the whole IE9 "experience" to mobile devices, so what you can expect from IE9 mobile is the same as from IE9 on desktops.</p> <p>Well, that's not entirely true. There are some more differences:</p> <p>What's in IE9 mobile that's missing from the desktop version</p> <ul> <li>viewport meta-tag, well almost. The scale properties are not supported (which is a bummer)</li> <li><code>-ms-text-size-adjust</code> is added as CSS property, works the same as <code>-webkit-text-size-adjust</code></li> <li>GPS support for geocoding</li> </ul> <p>What you will miss in IE9 mobile</p> <ul> <li>Downloadable fonts. Font face is supported, but fonts aren't cached</li> <li>Cross window communication</li> <li>CMYK images (do we need that?)</li> <li>Streaming audio. And that leads us to <a href="http://www.fettblog.eu/blog/2012/04/08/html5-audio-on-mobile-devices/">this issue we had to face once</a> </li></ul> <p>Actually, it's quite nice that the mobile version of this browser behaves <strong>almost</strong> the same as the desktop browser, which makes developing alot easier. On the other hand, I think a touch device, and thus a browser on a touch device, is much more different than your average desktop. So we also need a certain differency in behaviour! The features added by the touch event on mobile webkit browsers allow us much more flexibilty and possibilites for our web apps. Furthermore, it's just wrong to treat your finger as a mouse.</p> Robust (but hacky) way of portrait / land­scape detection 2012-04-16T00:00:00+00:00 http://www.fettblog.eu/blog/2012/04/16/robust-but-hacky-way-of-portraitlandscape-detection <p>On mobile devices it's pretty easy (and in some cases also pretty cool) to change the look of your website respectively to the orientation of your device with media queries. However, sometimes the orientation does not only affect your layout, but also the routines of your Javascript. This article shows some possibilites how to detect portrait/landscape orientation on your mobile device. Some are less robust considering multiple device vendors, and some are in exchange a lot more hacky. You're gonna love it.</p> <p>When searching for portrait/landscape orientation detection we mostly get the following, well known results on Stackoverflow and consorts:</p> <!--more--> <pre class="prettyprint"><code class="language-javascript">window.onorientationchange = function() { switch(window.orientation) { case 0: //do portrait stuff break; case 90: //do landscape stuff break; case -90: //do landscape stuff break; case 180: //do portrait stuff upside-down break; } }</code> </pre> <p>Pretty easy to understand: When the device changes it's orientation, check the degrees of your device, and in case of 0 or 180 degrees it's portrait mode, landscape mode otherwise. This is true for almost all mobile phones out there and was heavily popularized by Apple and it's <a target="_blank" href="https://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html">Mobile Safari Web Development Documentation</a>.</p> <h2>Samsung Galaxy Tab 10.1 and consorts</h2> <p> The new Samsung Galaxy Tab (and some other devices like Toshiba Thrive) however, do things a little different: Here <code>window.orientation</code> results in 0 degrees respectively 180 degrees in <strong>landscape</strong> mode. </p> <p>And acutally, that's okay. That's how the Galaxy Tab should be held by the users. That's it's zero degree orientation by definition. It's just a pain in the ass for us developers.</p> <h2>matchMedia for media queries in Javascript</h2> Media queries are cool for device detection because they're really checking for portrait and landscape orientation, not for the devices degree orientation. And there is actually a way to use media queries in Javascript. Credits to the code snipplet go to <a target="_blank" href="http://davidwalsh.name/orientation-change">David Walsh</a>. <pre class="prettyprint"><code class="language-javascript">var mql = window.matchMedia("(orientation: portrait)"); // If there are matches, we're in portrait if(mql.matches) { // Portrait orientation } else { // Landscape orientation } // Add a media query change listener mql.addListener(function(m) { if(m.matches) { // Changed to portrait } else { // Changed to landscape } });</code> </pre> <p>However, <code>window.matchMedia</code> is not support prior to iOS5 and Android 2.3. </p> <h2>Hacky Media Query solution</h2> <p>But there's another way to bring media queries to your Javascript for this use case. By its roots, the CSS file:</p> <pre class="prettyprint"><code class="language-css">body:after { content: ""; position: absolute; color: transparent; } @media all and (orientation: portrait) { body:after { content: "p"; } } @media all and (orientation: landscape) { body:after { content: "l"; } }</code> </pre> <p>We create a <code>body:after</code> pseudo element (because it's always here and doesn't require an extra DOM element) which is placed absolutely (so it doesn't take any space) and has a transparent color (so we don't see its contents). According to our orientation, we change the element's content to respectively.</p> <p>And this is our Javascript:</p> <pre class="prettyprint"><code class="language-javascript">//get style of the body:after element var bodyAfterStyle = window.getComputedStyle( document.body, ":after"); window.onorientationchange = function() { if(bodyAfterStyle.content == 'p') { //do portrait stuff } else if(bodyAfterStyle.content == 'l') { //do landscape stuff } }</code> </pre> <p>Pretty self explanatory. I know that there're other ways, like calculating with the device window width and height, but there might be some issues because the windows width is defined by its content. So I think it's better to stick with one of the solutions above.</p> <p><strong>Bottom line:</strong> Know your devices and if possible, use <code>window.matchMedia</code> for your orientation detection. </p> HTML5 Audio on mobile devices 2012-04-08T00:00:00+00:00 http://www.fettblog.eu/blog/2012/04/08/html5-audio-on-mobile-devices <p>HTML5 Audio on desktop browsers is a mess. But you haven't experienced true pain and suffering until you try to get HTML5 audio done on mobile devices. This article is a follow-up to <a href="http://www.fettblog.eu/slides/HTML5Mobile">my talk at the Barcamp in Salzburg</a> in March and was inspired by <a href="https://twitter.com/#!/codepo8/status/187790267464679424">Chris Heilmans tweets</a> on that topic a few days ago.</p> <p> First of all: We all know <a href="http://www.modernizr.org/">Modernizr</a>, we all use it and we all love it. With Modernizr we made a huge step away from applying features by browser detection towards feature detection. This is not only the recommended way of building websites and web-apps, but even more a mindset or way of thinking: Ask your browser what he can do (do a <strong>test</strong>), and you know what you <strong>can use</strong>, absolutely independent of browser vendors and versions. </p> <!--more--> <p> For such a complex feature like HTML5 Audio, it's great that with <a href="http://areweplayingyet.org/">areweplayingyet</a> there is a community out there which supplies such tests for HTML5 Audio subfeatures, API calls and best practices like hot swapping audio sources. </p> <p> If you run through some tests and view the browser compatibilty list, you will see that many tests fail on mobile browsers like early Android implementations and especially on iOS devices. But why? Is HTML5 Audio compatibility really that bad on iPad and iPhone, especially when it's the only way to include sound? </p> <p> Well. Yes and no. HTML5 Audio is pretty f***ed up on iOS too, but it works. And if you know the rules, you know why all tests fail and how you can deal with that. </p> <h2>#1 - One does not simply play sound on JS event</h2> <p> The most important rule of all: Do not think that you just can play sound by using JS events, like you're used to when working with Desktop browsers. Each sound playing requires a user interaction, his "OK" or his "Go for it" by touching an element on your page. This can be the built-in Audio player controls (which seem to be not compliant to Apples user interface standards by being flipping small), or any click or touch eventhandler bound to your elements. Once an interaction is done, the file is downloaded to your device. After that, you can do all the cool audio things you want to do. </p> <p> But why do you need user interaction to play audio (and video)? Isn't that unusual? Might be, but imagine yourself with your iPhone in a foreign country, having limited bandwidth, trying to reach your favourite website which just came up with some nifty 10MB background song not only trying to ruin your nerves, but also adding some more bucks on your phone bill for next month. User interaction seems legit now. </p> <p> Possible solution for that: Have a splash screen in your app, that the user has to remove, and that triggers the download of your soundfile. Quirky and awful, but so far the only way at the moment. </p> <h2>#2 - There can be only one!</h2> <p> Classy old Immortal rule: You can't play more than one sound file at once. So no background music and laser buzzes in your star shooter. Personally I'm okay with that. But how about making use of that? Think of "There should be only one!". Enter sound sprites.</p> <p>Image sprites are well known: Having one image with all icons and stuff, and accessing them by putting the right background-position in your CSS file. Sound sprites work similar: Put all sound you want to play in one audio file, and define start and ending indices. Now the workflow is as follows:</p> <ul> <li>User removes splash screen, downloads file</li> <li>A few seconds of silence are played, right at the start and right after interaction</li> <li>After that, we should have direct access to all positions in the whole file. Just jump to your positions as you like</li> </ul> <p>Below is some sprite demo I came up with in my talk, but also take a look at <a href="http://thewebrocks.com/demos/audiosprite/">Chris' more sophisticated solution</a>.</p> <pre class="prettyprint"><code class="language-markup">&lt;audio src="sprite.mp3" controls="none" id="myaudio" /&gt;</code> </pre> <pre class="prettyprint"><code class="language-javascript">var maudio = document.getElementById('myaudio'); var soundSprite = [ {start: 0, end: 3000}, {start: 3500, end: 6789} ]; element.addEventListener('touchstart', function(ev) { maudio.play(); playSoundFile(0); }) function playSoundFile(idx) { maudio.currentPosition = soundSprite[idx].start; var x = setInterval(function() { if(maudio.currentPosition &gt;= soundSprite[idx].end) { maudio.pause(); // There is no stop() in HTML5 clearInterval(x); } }, 50); }</code> </pre> <p>Soundsprites are pretty common now, and well supported by libraries like <a href="http://www.schillmania.com/projects/soundmanager2/">SoundManager2</a>.</p> <p>Think back to Modernizr: We now know that iOS devices support HTML5 Audio, we know that we can do a lot with that even if the tests fail. Can you adapt your tests? Is it okay to have the user interact with your app before testing? Is it okay to download test files? Or do you just go back to user agent sniffing to wrap up your target platforms?</p> <h2>Internet Explorer 9 Mobile</h2> <p>The things learned above apply to most mobile Webkit implementations, but we have to deal with another browser as well: IE9 on mobile devices</p> <p>IE9 doesn't support a lot HTML5/CSS3 features, but the features that it supports are usually really well implemented and robust. Same goes for HTML5 Audio, which also has been stated by the developers of <a href="http://cuttherope.ie/">Cut the rope</a> in a <a href="http://nerdplusart.com/why-is-there-flash-in-the-html5-version-of-cut-the-rope">blog post</a> some months ago.</p> <p>Same goes for the mobile version of Internet Explorer 9. And usually, you can expect everything that works in IE9 will also work in IE9 mobile. Same goes for sound, we even don't have to wait for user interaction when playing and downloading files! Although I don't know if that's a good thing or a bad one.</p> <p>However, there's one thing that doesn't work on IE9 Mobile, and that is changing your sound "position" when playing, so sound sprites won't work there. Now imagine a web-app exclusively covering IE9 mobile and iOS, I wonder how tests are going to look like there, or if you just go back to user agent sniffing.</p> <p>Bottom line: HTML5 Audio is a wreck at the moment, and don't get fooled by "support". Just because your devices supports a certain feature does not mean that it's usable in the way you are used to.</p>