Google I/O 2011: YouTube’s iframe Player: The Future of Embedding

Google I/O 2011: YouTube’s iframe Player: The Future of Embedding


Wilkiewicz: Hello, everyone.
My name is Jarek Wilkiewicz, and I work for YouTube.
I’m a developer advocate. Um, I have Greg Schechter
and Jeffrey Posnick here with me today. And, uh, we’ll talk about, uh,
YouTube iframe player and the future of embedding. So thank you very much
for coming. Um, there’s another one
offered today. Um, we will talk about, you know,
what the iframe player is and why we introduced it. Really what is the problem
that we were trying to solve? Um, Greg will cover, uh,
HTML5 Video Playback, some of the challenges
that he has faced implementing the player, as well as the actual API, um,
design and implementation. So if you’re thinking
about introducing you’re own JavaScript-based API
on top of an iframe, he’ll highlight
some design choices that–that you might consider
in your own designs. Uh, we’ll talk a little bit more
about, you know, what are the differences
between the, um, iframe JavaScript API that we expose now and the existing ActionScript 3
JavaScript API, and what this means
to application developers. And then finally, Jeff created a very nice
sample player application for this occasion. And he will walk you through
the implementation of–of his HTML5-based,
uh, video player, so that you could reuse
that code in your own applications. Uh, before we start, um, if you would like to
live-tweet about this session, here are the hash tags– #io2011, #YouTube. And then we would like you
to submit some feedback for us. The QR code is right here and the show uplink
as well on this page. So, uh, you know, this is the summary
of the session, really. Uh, you know,
what we’re trying to do is–is to let the embed,
not the embedder figure out the complexity
of web video. Uh, so as you all know,
what we’re facing is really a platform-
fragmentation problem right now. There’s a lot of new platforms, new operating systems
coming online, especially in the–
in the mobile space, so then in order to provide, um, platform-independent
portable video playback, uh, the embedder actually
is facing quite a few choices. And we’re trying to simplify
this complexity. So, uh, as I mentioned, both– you know, we see new platforms,
and the encoding standards supported by these
platforms also vary. So we have H.263 to H.264, uh, WebM VP8, and so forth. And then the actual
embed technology also, um, is different
depending on the platform. So historically
we had AS2–AS2. Um, there’s some RTSP streaming
support for feature phones that we still have,
AS3, HTML5. So all that actually adds
complexity whenever one wants to include
video in a web page or web application
or a native application. So the problem that we’re
trying to solve here is to really provide
a simple embed that will work across
all the range of platforms and hopefully future-proof
the embed, so once you embed a video
in your application once, as new platforms come online, we’ll take the responsibility
of making sure that the video playback
is really designed for that platform,
works well, and you don’t get a black box
saying, you know, you need to install this
or that plug-in and so forth. In the lower left
I have a syntax example of the iframe player, so for those of you
who have used it before, it should be pretty familiar. For those of you that haven’t,
one thing to notice– there’s nothing here about the underlying
video implementation technology. So just by putting
that snippet of code in your web application,
web page, whatever, uh, the only thing
it instructs us to do is to actually choose the optimal
video playback technology for your device with an encoding that your
device can actually support and play the video back, so you really don’t have
to worry about the complexity. Uh, so next we’ll talk
a little bit more about the design decisions that drove the iframe player
implementation, as well as, you know,
how we ended up exposing the player API
to application developers, much like what we have done with the Flash player
in the past. So now I will hand it off
to Greg, who–who will dig
into the details. Schechter: Thanks, Jarek.
Uh, so I’m Greg Schechter. I’m one of the engineers
that works on the HTML5 player and the iframe embed. And, uh, you know, HTML5–
it’s new. It’s awesome. You’ve been seeing
all these cool demos. But Flash has sort of been
in the business for quite some time. Uh, so we kind of
wondered, like, why should we build
this HTML5 player? We’ve got this great Flash one. Uh, you know, what makes
HTML5 better? And so we looked–we took a look
at, like, both platforms, and we looked at performance,
accessibility, something I call
“device-ability,” and all sorts of things, and tried to compare
the two, uh, platforms. So the first thing
we sort of looked at is, like,
the different features. Uh, what does Flash provide, and what does HTML5
have as well? So one of the first things
that’s really important that Flash has
was robust video streaming. In order to provide this really great,
excellent user experience, we need to have, uh,
fine control over buffering and being able to change
the quality. Uh, we also want
to be able to jump to any part of the video,
when the user’s seeking. And HTML5 doesn’t quite have
this fine-tuning yet. Another important thing
is content protection. Uh, we don’t own
all of our contents. Uh, you might have heard that, uh, we just launched
all these great video rentals. And, uh, we’ve got to make sure
that our content is secure. And so, uh, Flash has
a protocol for this, RTMPE, that’s, like,
built in, easy to do. And HTML5 doesn’t have an accepted standard
for this yet. After that,
the sort of full screen– uh, when I’m watching
my favorite cat videos, one thing that’s
really important is I want them full screen
and in HD. And, uh, we–
we don’t really have an API to do this yet. WebKit does have something
in its nightlies. And, uh, and the code for that
is actually pretty simple. It works pretty well. Uh, and so it looks
a little bit like this. You can grab any elements. Uh, so in our case,
we’d grab our video player. And we would request
that it goes to full screen. And then once it gets triggered, we can do
whatever we want with it, you know, make it larger,
change our buttons, and–and so forth. And so eventually,
because this is in WebKit, this will be available
in Chrome hopefully very soon. Uh, another thing
that’s important is camera and microphone access. A lot of our users come to–
come to YouTube, and they just want to film,
uh, talking to, you know, the–the millions of viewers,
uh, right there. And, uh, HTML5
doesn’t have this yet. Uh, but Flash–
you know, it’s there. Another interesting thing
is formats. So with Flash,
it’s pretty much gonna– if–if the device
supports Flash, you can play your Flash videos
without any issues. Uh, HTML5–we actually
have to support two different formats.
We have H.264 and WebM. And so browsers, uh,
some browsers will support both. At the moment,
Chrome will support both. But you know, then you get
browsers like Firefox, which will only support WebM, and IE, which will
only support H.264. Now we have, uh, all of our
videos are encoded in H.264, and most of them
are in WebM at the moment. But it’s taken a while, ’cause
it’s such a large database to get through and make
all these additional encodings. Uh, so now why is HTML5
so great? And, uh, it’s–we’ve got
this open-source technology– open source browsers,
players, and codecs, which really allows
for some great, uh, development in the space, and making it really fast-paced
and changing really quickly. It also should have
lower latency when we’re watching
these videos. We don’t have to start up
this plug-in. It’s just native in the browser.
It should be a lot faster. And this is really evident,
uh, in the iframe, which I’ll–I’ll show later. And, uh, so we’re–we’re hoping
for better performance, uh, with these
different encodings. Uh, we were hoping to get better
performance for the videos and just fidelity. Just it works smoother
without worrying about our code screwing up
the video playback. Another great thing
is accessibility. And, uh, there’s a few other
accessibility talks here that go into some
really detailed stuff here. Um, one of the–the talks
that happened earlier which was great
was all about captions. And, um, you know,
I really like having captions on my–on my cat videos. This is one of my favorite
comic strips there. But, um, so one of the features
that’s important to me is actually navigating,
uh, in the browser. And because it’s native, I can just tab around
in the page. So let me show you
a quick demo. Um, so this is the– you know, this is YouTube,
and I got the Flash player here. And I start
tabbing around the page, and, uh, eventually I’m trying
to get into the player, but I just sort of jump
below it, and I’m– my tab focus is down here
at the moment– um, and I can’t get
into the player. But if I click my mouse on it, um, you know, I’ll–
I’ll give focus to the player. and then I can tab around
and use the controls, um, but I’m trapped.
I can’t get outside. I can’t start to interact
with the rest of the page again. One of the great things
that’s great about HTML5 is, uh, I can tab around
in the page, and then I can jump
right into the player, and I can use my keyboard
to start playing the video. Um, and then if, you know, I want to move on
to the rest of the page, I can just continue
to tab around, and, uh, and I’m out
interacting with the page again. And so, um, you know,
different user agents are gonna have, um,
have to build, uh, APIs for having these–
this special video handling, uh, for these–
for the video tag. Um, but then screen readers,
any screen reader you have will be able to access that, and you’ll have, you know,
accessible videos. Uh, so next thing
I want to talk about is device-ability. And that’s–basically
what I mean by that is where do these platforms
that work in Flash– where can you watch
Flash videos, and where can you watch
HTML5 videos? And at the moment, when we look at our
HTML5-capable browsers, uh, you know,
we’ve got just under 40% of, uh, of browser usage
is HTML5-capable. Uh, Chrome
is really kicking butt in this space as well. And a very small percentage
of this is mobile. So the world kinda
looks like this. There’s all these places
that support Flash, uh, a good chunk
that also support HTML5, and this tiny sliver– and this is exaggerated
a little bit– that only do HTML5, and that’s mostly iPhone
and iPad. There’s a few other
random things that are in there as well. Uh, but then what’s
really interesting is when we look
at actual usage of people who are getting access
to our APIs, our YouTube APIs, and where
they’re playing videos, uh, it actually kind of
shifts opposite. Uh, most of the places
are actually on devices that don’t have Flash. Uh, so that’s, like,
you know, really great that, uh, we need
this HTML5 support there. Uh, so we were asking
why HTML5, but it’s really about when
we should be using HTML5, uh, ’cause we clearly see
that it’s– it’s needed in many places. Uh, so our first primary goal
about building this player was we wanted
to recover playbacks that would have been lost
without Flash. And so our solution for this,
uh, to make it real easy, was to have this iframe embed. So you know,
like we showed earlier, the code for it is very simple. You just plop in the–
the video ID, and, uh, and you’re good to go without having to worry,
uh, about anything. And so this has, you know,
great advantages, because it gives our user
HTML5 or Flash based on the device
and the user’s preferences. It allows for so much better
mobile support. And it gives us this
“just works” experience. Uh, so just a little bit
about how you would get the HTML5 player. Uh, at the moment,
you can opt in, uh, to getting the player. And if you go
to youtube.com/html5, you can join the experiment. And if you’re trying to view
these videos on a device that doesn’t support Flash, you’ll also be opted in
automatically. And, uh, so the first thing
we do is we check, all right, does your browser
support HTML5? And the code for that
is pretty simple. We just create a video tag,
and we see– can we play the two formats
I mentioned? Uh, so the first one is H.264, and the second one is WebM. Now not a lot of our, uh, not a lot of our embeds
actually get clicked on to play, so we don’t load all the data
about the video yet. So we don’t know
if we have both formats, uh, when we–
when we load the video. Uh, so that’s why we have
to check both at the start. Um, after that, we wait
for the user to click play. We fetch this information. And then we say, all right,
are there any business reasons that we can’t play the video, um, the details of which
are not all that important? Um, but if we–if it’s great,
if we can play this video, then we’re gonna look
and we’ll see, do we have, uh,
a format that’s available, uh, that this browser can play? And if yes, great,
we’re winning. Otherwise we’re gonna fall back. We’re gonna go
to that Flash player, or we’re gonna fail out if your
device doesn’t support it. Uh, so the next thing–
once we have these two players, we can look at performance
between the two. And, uh, so the first thing we–
the first one– pretty much one of the most
important things is the player start time. And this is the time,
uh, just so that it looks like the player’s happening. Uh, maybe you–like,
on the main page you see, like, the little loading icon, or at times you’ll see
the thumbnail, um, on the–
on the iframe embed. And so you know, it’s the first
cue that the user has that’s something’s working,
something’s interacting. And at the moment,
we can see HTML5 is just a lot–
you know, a lot faster. It’s a full second–
a full half a second faster, which is great. Um, and so I pulled out, like, this little,
uh, strip line thing that–that’s got
all these photos. And this was
in a controlled environment, and the times got
exaggerated a little bit. Um, but the comparison
is pretty accurate about how much faster
the HTML5 player is just to show
that initial thumbnail. And so, uh, I want to–
I want to show you it, uh, in action now. So I’ve got, uh,
the two players here. The top one’s the Flash player, and the bottom one’s
the HTML5 player. And, uh, when I reload
the page, we can see that the HTML5 player
comes in a lot faster, which is great. And then when I actually go
to play the video, uh, sometimes…
Chen: Hi, everyone, My name is Steve Chen.
Schechter: Um… Chen: I’m one of the co-owners
of YouTube, and I run the engineering team here.
Schechter: I don’t know. Stearns: Hi, my name’s
Jeff Stearns– Schechter: I don’t know
what’s going on with the Flash player today,
but that’s another reason I like the HTML5 player more.
[laughter] Schechter: Um, but you can
see that the, um, the HTML5 player started
a little bit faster. And I actually think
that I just got lucky there, ’cause a lot of times the Flash player
will start first, um, so we’re still sort of
tweaking performance on our end. Um, one of the advantages
that the Flash player has is because all the formats
are the same, it sort of can take advantage
of caching those formats a little bit better. Um… Uh, so the next thing is,
like, you know– that I want to talk about
is the details of our new JavaScript API and how, you know,
you control the player. And so since we have this new– this new way to embed videos,
this iframe embed, you know, we had to write
a new API to interact with it. So the most important thing
was about communication. We don’t have this object
in the– we don’t have an object
in the DOM anymore, uh, that we could just, like,
add things to directly. So, uh, we had to think of a way that this API could communicate
with the player and this iframe. So the first thing we thought of was to pull the URL fragment. So that’s just a little, uh,
fragment at the end of the URL. And you can–you can update it. It won’t cause the page
to refresh. And there were a couple
problems with this. Uh, the messages
are very one-dimensional, so it’s hard to send
complex messages. Uh, polling is gonna eat up
your CPU, and it’s not gonna be
instantaneous, uh, ’cause you’re being–you’re
gonna have to set a timer. And, uh, then you have, like,
of different timers conflicting, and it’s just a little bit
of a hassle to manage. Uh, and then the really
difficult part is since both directions
of communication, uh, sending messages to the player, and the player sending messages
back out the page– have this–have to use
the same fragment. And so then you run
into issues–all right, our– you know, am I reading data,
or am I writing data? Am I overwriting data
I haven’t read? And it just gets
very complicated. Um, so instead we decided
to use the postMessage API. Now this isn’t available
in as many browsers as just being able to pull
that fragment, um, but it works a lot simpler. And the majority of our–
of our users will be able to use,
um, postMessage to communicate with the player. So it’s very simple. Uh, basically
you have a window. You just send a message, um, and you specify the–
the target of your message. And so, uh, we basically
can use JSON to encode and decode
our messages, so it’s very native
to the browser. Uh, we don’t have
polling anymore, uh, so we can use the native
event listeners to communicate. And, uh, the communication
is sandboxed per window. So, uh, you know, they’re using
different channels, but now all the messages are being sent
to the parent windows. So if you have multiple players
on the page, it gets a little complicated,
but we were able to, uh, easily, you know, differentiate
between the different play– iframes on the page. And one interesting thing is
the calls are now asynchronous, uh, so if you wanted
to read data from the player, uh, you know, you might have
to wait some time. so instead we just store
the player– the player state for you. And so when you interact
with the player, you’ll get those–you’ll get
that data that you want, uh, right away. Uh, so now I’m gonna hand it
back over to Jarek, who’ll talk about, uh,
comparing the iframe API to the AS3 player API.
Wilkiewicz: Thanks, Greg. So Greg went through
the design decisions that went
into the API implementation. And I wanted to talk
a little bit about, you know, what this means
for an application developer that is used to interacting
with our player API in the AS3 world and what the differences are, how you can take advantage
of the new API and what other things
to watch out for. And then, you know, to prove
that it is in fact possible to use the API and create
very interesting experiences around YouTube Video Playback, Jeff is going to follow up
with his example after my, uh, part. So for those of you that have
worked with the player API, uh, you know,
this is very straightforward, but I just wanted to refresh
your memory. So there are three ways
in which YouTube Video Playback can be controlled
by an application developer– through player parameters,
ActionScript API, and JavaScript API. So let’s walk
through the three mechanisms and see how they differ
if you are interacting with the Flash
versus HTML5 player. Um, so when it comes
to player parameters, there’s really two cases. One is–down the line
video implementation is based on Flash. And again, the decision
that drives this is something that Greg
has described before, so he had this nice,
uh, decision tree that was deciding
whether to, uh, start HTML5 Video Playback
versus Flash Playback. So if it’s actually
Flash Playback, then all the player parameters that you are used
to taking advantage of– it’s just passed through. So they work just like before. And here’s an example
of a player parameter. This one will trigger the video
to start playback as soon as somebody navigates
onto a page where the embed is present. Uh, if the underlying
implementation is based on HTML5 video, then we are subject
to some of the limitations, uh, that Greg has described as well as some of the features that we haven’t frankly
implemented quite yet. Uh, so here’s a–
you know, an overview of things that, uh, are–
fall into these two categories. I’ll just walk
through them quickly. Uh, one thing to watch out for,
um, on iOS is the autoplay
player parameter. It’s not really honored,
so if somebody embeds a video, and then an iOS device
navigates to it, uh, then the user
actually has to confirm the playback selection. And I guess it’s just to protect
somebody’s data plan from getting taxed. Even though
the application developer would like the video to start
playing right away, iOS will not let that happen
until the user selects play. Uh, caption support– our HTML5 player
does display captions, but there are some categories
that we haven’t implemented yet. For example, the ASR captions
are not, uh, shown yet, but this is something that is
under active implementation. Uh, full screen support is not quite there yet. Uh, the limiting factor here
is the browser standardization. And Greg mentioned, uh,
the work that is done for WebKit to make that
available, and hopefully you will see that
across the board, but don’t count on it yet. Uh, annotations–another
very popular YouTube feature, is not implemented
by the HTML5 player. Uh, I expect Greg
to start doing that as soon as he gets back to
his office after this session, because it’s
a very popular feature. People have done
amazing work with it. And then finally,
related videos– uh, you know,
if you watch a YouTube video, uh, at the end of the playback
you will see a bunch of videos that we think
are of interest to you. Uh, that functionality
can be disabled, uh, through the rel parameter. Uh, that is something we don’t
have in HTML5 player yet. Um, so next let’s talk
about the ActionScript API. So–so for those of you that have built, um,
custom Flash players, um, what typically
people do is– we actually offer
a Chromeless player that is based on, uh, AS3. That player has an API
that can then be used in a Flash application. And then you can build
your own experience around the core YouTube
Video Playback experience. So if you don’t like, you know,
the way our controls look, any kind of aspect
of the user experience, you have full control over that,
and just invoke API methods. So, uh, you know,
for the purpose of this talk, this is not, you know,
entirely applicable, because, uh, we’re talking about
the iframe API, and the only API
that we expose to the iframe is actually an HTML, um,
JavaScript-based API. One thing to note is, um… you know, in the new world
of mobile devices that may not necessarily
support, uh, Flash– you know, iOS is
a good example of that. For those of you that are
building applications that get mobile use,
you know, you probably noticed that this is a very
nicely growing area. For everybody, mobile
applications are quite hot. Uh, so you know,
if you invested time in building a Flash-based player around our Chromeless player, the bad news is some platforms
will not support it. So all you will get
is a black box. Uh, so it’s a little bit
of a bummer. Jeff has a remedy for that, as he will show
later in the session. But this is one of the things
that–that you run into, and very quickly if you actually
build mobile applications. And people that have devices that don’t support, uh,
the Flash technology actually navigate to your page that has a very nicely crafted
custom player that you have built that–
you know, it no longer works. Uh, so you know,
instead of that, what you can do is use
our JavaScript API for the iframe player. And, uh… whoops. Um, let’s just quickly go
through the differences between the AS3
JavaScript player API and the iframe
JavaScript player API. So there’s three operations,
three categories that I would like to describe–
the player init, the actual methods
that we expose, and the event handling. So player init
is quite different between the, um,
AS3-based player and the iframe player. I have a side-by-side example. Hopefully you can see that. But, uh, really,
on the left-hand side you see the new, uh, JavaScript-based API usage that we exposed. You can load the API library
asynchronously and then create instances
of YT.Player, which is really
the player object that allows you to then control
the player behavior. When you instantiate the player,
you can specify a video ID that you would like to load,
as well as any player parameter and, uh, event handlers. So there’s a set of events that we allow application
developers to subscribe to. If you would like to do that, you can actually specify
the handlers for these events right when you
instantiate the player. And on the right-hand side,
this is the, uh, old-school AS3 JavaScript API
initialization, uh, using the SWFObject to load the Chromeless
player library. And the event handling
is actually done through DOM event registration. It’s slightly different. Uh, that method also works
in–in, uh, the iframe player
JavaScript API, but now we have
a convenience way of– convenient way
of creating the handlers right when the player
is instantiated. Um, so when it comes to the actual
core player functionality, um, there is five
kind of major groups of operations that we expose. So queuing functions would allow
you to load video, queue video. Uh, playback controls,
player settings– you know, you can set
the volume of the player, navigate to a specific
time stamp in the player. Uh, playback status operations that tell you more
about, you know, the actual–what’s happening
in the actual video. Uh, playback quality–
so you can actually, uh, override the selection
that we make on behalf of the user, um, when it comes to, you know, what is the optimal,
uh, resolution that we should be trying
to, uh, stream to the user. So, uh, by default we actually
try to detect, you know, what is the device capability?
What is the size of the window? And then, you know, for example,
if HD doesn’t make sense, we won’t be trying
to ship HD–HD. But if you want to override it, you can do that
programmatically. Uh, and then finally,
uh, we provide some additional
metadata information, such as the duration
of the video, the embed code for the video
through the API. So comparing these two groups
of operations, really everything works
pretty much the same. Uh, the playback status
is the only area where we still have
some work to do, so right now
the getVideoBytesTotal returns a hard-coded 1,000. Uh, and that is hopefully
going to change in the future. But other than that, you know, all the other operations
pretty much work the same. Um… So a quick note
about event handling. Uh, as I mentioned previously, uh, the way you initial–
initialize the handlers is slightly different, uh, but the actual, uh,
semantics are very similar. The only note
that I wanted to make is that, uh,
today if you are counting on the YT.PlayerState.BUFFERING
event or the state to be
actually delivered to you, we don’t actually generate
that yet in HTML5 player. So if the underlying
video playback is based on HTML5, you will never actually see
that, uh, state. Other than that, uh,
there are some, you know, syntactical differences with respect
to event registration. So the AS3 player API relied on the kind of typical
DOM-based registration, whereas for
the HTML5 player API, uh, what we recommend is using
the event registration that is conveniently
possible through– as a part of the player init. And, uh, one note
that I wanted to make is that if you would like
to, uh, kind of play around with the API, for those of you that have used
Chrome Console, uh, you know all about it. But for those of you
that haven’t, uh, you can very easily,
you know, learn more about, you know,
what these, uh, methods are and, uh, invoke them
and so forth, using the console
right from the browser window. Just again, a quick way
to get up to speed on, you know,
all the functionality that is exposed. And I believe we have
actually a session about, uh, Chrome Dev Tools at I/O. So if you haven’t caught it,
that’s a good recording to catch up on later. Okay, uh, next I will
hand it off to Jeff. And Jeff will talk about
his experience with the API and prove to you
that you can in fact build very interesting video
experiences around YouTube using the new, uh,
JavaScript API and HTML5 Video Playback. Posnick: Thanks, Jarek.
Um, so I’m a member of the YouTube
API developer relations team. And part of my job is to help developers
use these APIs, so I certainly wanted
to familiarize myself with the iframe player API. And, uh,
this example application I’m gonna show off is, uh, something that I did to get familiar with the API, but also, um,
just hopefully something that will help you guys learn. And I wanted to share
some best practices that I picked up
while writing code that hopefully I’ll be able
to share with you. So this example, uh, provides basic YouTube
feed player functionality. Uh, and what I mean by that–
uh, think of it kind of like a playlist player, but instead of only playing back
YouTube playlists, you can play back
any feed of videos. And it’s powered
by the YouTube Data API. Uh, this is kind of
a separate set of restful APIs that can be used for interacting
with YouTube in a variety of ways. One of the ways
is to retrieve feeds of videos. So you can, uh,
for instance, plug in a search term and get back a list
of all the videos on YouTube that match
that search term, or you can get back a list
of videos that are uploaded in given users’ accounts,
for instance. So this particular example, uh, makes use
of some modern web technologies, as you might expect. Uh, there’s a bit
of HTML5 in there. Uh, a lot of JavaScript
is going on under the hood for interacting
with the iframe player API. And you know, there’s some CSS
as well. And, um, hopefully, you know, as I said, this is useful
in its own right. Feel free to take a look
at the code and, uh, you know,
use it and adapt it in your own applications. But you know, my main purpose
was to illustrate, um, some iframe player usage
best practices. So I just want to talk
in a little bit more detail about the different components
that go into this demo. Uh, obviously on the HTML5
side of things, the video element
is quite important. It can be used
for the supported videos. And this again is based
on that kind of flow chart that Greg was
going over earlier. Um, so assuming a given video
that we attempt to play can be played back
in the HTML5 video element, it’s going to use that. And we’re actually using
some of the player parameters that Jarek was talking about, uh, in this case specifically the controls=0 player parameter. So this is gonna give us
a version of the, uh,
YouTube HTML5 video player that doesn’t have
a type of– I guess “Chrome”
is the term that we use for it. It doesn’t have
the standard play button. It doesn’t have the seek bar. It just gives us
the video playback. And, uh, you know, we’re going
to actually implement the specific playback
functionality in HTML5 and CSS and so on. So, uh, to that point
we’re using SVG, which is scalable
vector graphics, and that’s just used
in a fairly straightforward way, just for implementing
the pause and play buttons in our example. And we’re making use
of. Uh, this is a fairly new
input element that can be used
for doing scroll controls, more or less. So, um, you’ll see it
in a little bit when I show you the example. But this is something
that allows you to drag and select a new value
for a given control. So Google Chrome currently
supports all those features that we’re using,
which is great, because that’s what I’m using
to demo this. But, uh, some browsers offer
just a subset, um, and in particular there’s not a lot of support
for, um, in a wide number
of browsers right now. And that’ll probably change
over time, but at the same time, you know,
feel free to, you know, adapt this and use
something different. There’s plenty
of open-source libraries for doing slider controls. Uh, that would
certainly work as well. Uh, as you might imagine, there’s quite a bit
of JavaScript involved in writing this sort of example. Uh, I’m using jQuery just as a matter
of personal preference for doing a lot of
kind of the heavy lifting. And one of the things that’s
nice about jQuery in particular is that it simplifies
the interaction with the YouTube Data API. Um, I’m not gonna
actually focus too much on the interaction
with YouTube Data API, but I definitely recommend
looking at the code, if you are curious
as to how you can write a web application
that will, you know, do read-only requests
to YouTube, get the results back,
and display them in some way. Uh, so it’s actually
gonna be using JSON-P under the hood,
which is a method of dynamically inserting
a script tag into a page and having that script tag point to a URL
basically on their servers. And the server knows
to respond back with kind of a wrapper that will invoke
a native function in our own JavaScript code with whatever the payload is
for the response. So we’re basically
getting around some of the cross-domain
limitations that would normally
prevent you from, you know, using something
like XML-HTTP requests to directly, you know,
ask our servers for a response. And jQuery will make that
pretty easy. jQuery, you know, also just makes a lot
of other things easy as well. And everything is JavaScript,
more or less. I’ll show you, you know,
the actual HTML source. It’s very short. Most of–all–
most of the important things are done
in the JavaScript section. And we’ll focus on some
specific sections there in a second. And there’s, you know, CSS,
as you might imagine– uh, very basic CSS styling. One thing I did decide to do
was just use, uh, Web Fonts, which are part of one of
the newer CSS specifications. And I’m using
the Google Fonts API just for pulling down something a little bit flashier,
let’s say, than, um, the built-in fonts that might be present on a given user’s computer. So this is the example. I have this open over here. Uh, I want
to just quickly load up the uploads feed for the Google Developers
YouTube channel. And Google Developers is
just a great channel in general for having a variety
of different developer content. So this is playing right away. Ross: My name is Max Ross. Posnick: And you see, you know,
these controls down here. This is SVG, as I mentioned,
um, yeah, right now, because for pause–
the pause button is disabled. The play button is enabled. Uh, these are
the slider controls using. If I do start the playback, we should be able
to toggle the volume. Ross: I focus primarily on… Posnick: And we should
also be able to use this to jump to a different portion
of the video. You know,
much as you’d imagine, you have the current time
listed over here. Um, just all the basic
type of controls that you would normally see
in the YouTube player, but implemented, you know, completely with our own code. And just to show you, we can also jump
to the next video in the feed. man: All right, hey, there. Posnick: You can do that
pretty much indefinitely. So just to show you really quick
the source, uh, for the HTML. Pretty straightforward–
it’s just this page of code. Um, most of logic,
as I said, is done in JavaScript. So I wanted to jump to that and show you, uh,
just a little bit of the JavaScript. And particularly,
I wanted to focus on the JavaScript that’s used
to handle player events. And, uh, this is kind of what I found the most, uh,
difficult to get correct. And–and when I’ve worked
with developers in general, um, and just spoken to folks, I think this is
the hardest thing to kind of wrap
your head around, how to respond to the proper
YouTube player events. And it’s really key
if you’re writing a custom player experience
to get this right. Otherwise you end up, um,
having, you know, UI elements that are disabled when, you know, they actually
need to be enabled or just, you know,
not taking into account the fact that an error has
happened and things like that. So, uh, there are kind of
three main characters– categories of events,
uh, that are handled via the YouTube iframe API. There’s the onReady events. Um, this is fired
when the player API is initially available. So if you wanted to do something as soon as the player API
is available, you know,
as Jarek mentioned before, the iframe API
right now is, uh, the way we recommend loading it
is asynchronously so, uh, it doesn’t necessarily– it actually would not be
available whenever, you know,
your DOM is fully available. It’s gonna be at some point
afterwards. If you want to put in some code
that gets fired only when everything
is fully ready, you would put it
in your onReady handler. The onError handler,
as the name suggests, is something
that would get fired whenever there’s any sort
of playback error. Um, for instance, if a video
is not playable on your particular device
for any number of reasons, uh, that will get fired. It’s pretty important
to put in some code for–for handling that. It might be as simple as moving
to the next video, if you happen to have
a list of videos, um, or displaying some sort
of error to the user. But it definitely helps
the user experience if you were to put in some code
to handle that. And the third category of events is onStateChange. Uh, you can kind of think of it
as a catchall that has a bunch of sub-events for the different changes
in the YouTube player state. And I wanted to focus most
of my time talking about that. So kind of five main events, uh, ENDED, PLAYING, PAUSED,
BUFFERING, and CUED. As Jarek mentioned,
the BUFFERING event won’t be fired
for the HTML5 player now, uh, but those other events
are all relevant. And… in general, um,
my recommendation is just not to make assumptions about the global state
of the player or what triggered the events
in your event handler. And you know, one example
where somebody could go wrong is they see that
the YT.PlayerState.PLAYING event was fired in their event
state change handler. And they figure, okay, well, if that event was fired, it must be because somebody
clicked on the play button. But there’s actually
a number of other ways that playback can start
for a given video. Um, the user might have clicked on the actual
YouTube video itself, which is something
that will trigger playback. Or you know, as Greg
was illustrating before, there are ways to use
keyboard controls for, you know,
tabbing into a video and starting playback. So if you have
any specific assumptions that are built around
the fact, okay, somebody must have clicked
on the play button, uh, they might be wrong i-in your play handler. So I would recommend,
uh, explicitly– you know, especially when
you’re changing the state of other UI elements
in response to events, I would recommend
to explicitly set the states each time through
in your event change handlers to whatever
the appropriate value you know it should be
for that event, rather than, you know, just,
like, toggling given elements. Uh, and I think,
you know, that leads to a much better
user experience. So I wanted to get
into some code that, uh, is relevant to that point, and just a few
helper functions here for enabling and disabling, uh, a list of different elements given their element names– element ideas, rather– and, uh, just a helper method for setting up the, um, the task that fires
in the background basically, that updates
the current time in– you know, what we saw
in the lower left corner of the player–
the current playback time. So we have, uh, something
that will set that up for us, ’cause we’re gonna need to clear
that in certain circumstances and then reestablish it later. So this is just, uh,
some snippets of code, uh, where you, for instance, would handle
the CUED state events. And this is what gets fired
when the player is load– the video is loaded
into the player, but playback hasn’t
actually begun yet. And in that scenario,
as you might expect, you’d think,
okay, what controls do I want to be enabled
in that case? And you want the play button
to be enabled. And what controls, you know,
just don’t make sense to have enabled? You know, pause, volume,
and seek– they’re not gonna
be able to do anything when you’re in that CUED state. So, um, you want
to explicitly change those values to those states, uh, each time
we get into the CUED state. And something, you know,
very similar– the thing
about the PAUSED state, except there’s gonna be
a different set of things that you’d want to have enabled. So you want
the play button enabled. You want the volume controls
to still be enabled, ’cause you can still change
the volume of the video, even though it’s not playing. And you’d still like the person
to be able to jump to a different point
in the video, so you want the seek bar
to still be enabled. But you know, again, you don’t want the pause button
to be enabled, because it certainly
doesn’t make sense. Uh, and you know,
and we’re also gonna clear the background time-out that, uh, that would be fired
to update the player time, because, you know,
if the video is paused, we know that the time is
not gonna be increasing at all. And PLAYING is in some respects
the opposite. We want to enable
the pause button, the volume button,
the seek button. We want to re-create
that interval. Uh, we want to set the volume and the duration values to whatever, uh, they are by reading that
via the API call. And you know, this might be
overriding the existing values. There might already be a value
for the duration there, or the volume
might already be equal to whatever
the current volume is, but it’s–it’s really not
gonna hurt anything to set that again. It might be wasting
a couple of CPU cycles, but I’ve found it– it just simplifies
things greatly if you don’t make
any assumptions about what’s already there
in the state, and you explicitly set it. And finally, ENDED– very similar, uh,
to what we’ve seen before. One thing I wanted to point
out… [clears throat] Excuse me. One thing
I wanted to point out is that it really makes
a difference, I’ve found, to explicitly set
whatever the current time is in the playback. And that’s the value
that you see over here. This is the current time.
This is the duration. Uh, once the video is over, you want to make sure that
that current time is set to the duration of the video. Um, you know,
I’m pretty sure folks have had the experience where you
reach the end of the video, and maybe it’s because
the video is not– it has, like, a fractional
second in its duration, and there’s
some rounding involved, and you know, it says
the video’s four minutes, but you’re at–the display
says 3 minutes and 59 seconds, and you’re like, what? What’s in that secret
one last second of the video? Obviously there’s nothing there, but it just leads
to a better user experience to explicitly set that. So that–
that’s my recommendation for handling the ENDED state. And yeah, that’s–
that’s kind of an overview of, um, some of the JavaScript
in the example. Definitely feel free
to take a look and explore some more. I want to remind folks
that we have those– excuse me. We have those hash tags– #io2011 and #YouTube–
for this session. And if you have any feedback, you can go to that URL as well. And we want to take
any questions you guys might have
either about iframe player API or YouTube topics in general. And hopefully we’ll have
some answers for you. Schechter: Um,
before we jump into questions, I actually want to promote– we’ve got this awesome
after-party coming up. Um, and so YouTube’s
one of the main sponsors for it. It’s at Thirsty Bear. You can
come grab tickets from us. And one thing that’s
really important is there’s a QR code
on the back. If you, uh, scan it
and register ahead of time, uh, you’ll get to skip
the line to go in. So hopefully you’ll join us
tonight for that. man: I have
a quick question for you. Um, I’ve used the iframe player
a little bit. Um, is there a way to– whether it’s Flash or HTML5 that’s being exposed on the–
on the client side, is there a way to, um, set the window mode
to transparent or interact with the CSS
of the, uh, the HTML5 video player? Schechter: Uh, yeah,
so, um, you can add wmode as one of the arguments in the, uh, URL. So that way you’ll interact
with Flash, and then you can just interact,
uh, with the iframe with CSS like you normally would, um, for–
if it’s the HTML5 player. man: Okay,
so with the oEmbed endpoint, I know that you can add
that parameter to get the iframe code returned. Um, are there plans to start
returning the iframe code by default
on the oEmbed endpoint, or not in the foreseeable
future? Posnick: Yeah, um, we actually recently made
a slight change to the oEmbed endpoint that ended up breaking
a lot of folks, and that was just changing
one character in the URL. So I think we’re–
I personally would advocate that we don’t make too many
changes to our defaults and continue to allow, uh, folks to explicitly request things in the hopes of not breaking too many existing
implementations. Uh, that’s–
that’s my personal preference. I’m not the one actually
responsible for the service. But that’s what I’m hoping. Wilkiewicz: One issue
that we’ve seen is, uh, you know,
people, uh, build platforms that don’t allow iframes. And then–so the iframe
actually has to be white-listed. And that has been kind of
the number one source of pain as we were rolling it out and working
with a lot of the, you know, say, blogging platforms,
provider, uh, you know, sites, and so forth, so that they will actually
allow this–this iframe. And we’ve made
a lot of progress, but, uh, there’s a lot more
work to do, yeah. man: Uh, hey, guys,
I had a question about the accuracy
of the, uh, the skip-to
and skip-ahead function. Um, I noticed
during the caption talk that was in here earlier, um, that there was
fractional seconds involved for the start time
and the end time for a given piece of text. Uh, my question is
are there plans to make it more accurate as far as the skip-ahead,
skip-back, um, just to make
navigational control a little bit easier, or to be more accurate
with, uh, any sort of external navigation
we can develop? Schechter: Uh, so one
of the nice things about HTML5 is we actually don’t have
that issue. Um, so with Flash you’re
worried about, uh, key frames and having to jump
to a key frame. Uh, HTML5 just standard– just goes to the time
that you specified. Uh, so you’ll have
that accuracy that you want. man: Um, can you–can you do,
like, fractional seconds or, uh, anything like that? Schechter: I, uh,
I haven’t tried it. man: All right.
Schechter: Um… I don’t know. man: Cool, thanks. man: Hi, guys.
Uh, the API documentation says that the iframe player API is currently not ready
for mission or business-critical
applications. Uh, I was wondering when,
uh, what the time frame for–for that to be removed is,
that restriction. Posnick: Yeah, I don’t think
we have an exact time frame. This discussion comes up a lot. Uh, I think just… the important thing to realize is it’s still a work
in development, and you know,
in the course of me writing this demo application, I know I’ve held
about half a dozen bugs against, uh,
against Greg… Schechter: Right.
Posnick: to–to resolve. So I-I think, um, you know,
it’s not there yet, and hopefully
that does not stop folks from using it
in their either, you know, “non-mission-critical”
environments or just trying to develop
against it in parallel to whatever
the existing thing is. And definitely file bugs. Let us know in, uh,
the Google group in particular. I think that’s the best way
of getting in touch with us. And you know,
Greg is super responsive about, uh, getting things fixed. And you know,
as we get to a point, I think, where there are
fewer and fewer reports of things going wrong, we’ll have a greater confidence
in, uh, you know, taking off that label and being able to say,
you know, it should be as–
as supported as our AS3 embed. man: All right, thank you. man: What’s the timeline on, uh, getting iOS support
with the autoplay? I know that’s an Apple issue
and not, you know, an iframe issue.
Wilkiewicz: Yeah. man: But when you’ve talked
to the team, have they given you a sense
of why they’ve disabled autoplay on the iframe player? And is there an E.T.A.
for getting that fixed? Do you see
a resolution happening? Wilkiewicz: So we–you know,
we work very closely with Apple. They’re obviously driving
a lot of our playbacks. And, uh, actually if you look
at the documentation closely, uh, for the video playback, it states that, uh, for 3G environments, uh, the user interaction
is required. And you know, I–personally,
I kinda, you know, buy that. man: Right.
Wilkiewicz: Uh, but then, on Wi-Fi, uh, you know, maybe it should be
a little more liberal. And in fact, this is what
we’ve been trying to, uh, to see if, uh,
the team at Apple could–could add that.
But I don’t have an E.T.A. But you know,
we have filed a– and there has been
a request for that. And we work very closely. You know,
they’re a good partner. man: Gotcha. But they seem
favorable to the idea that autoplaying in theory
would make sense on a Wi-Fi connection? Wilkiewicz: Uh, you know,
I-I wouldn’t go as far as saying
that they are favorable. Uh, but you know, if you cannot
look a the user documentation from our perspective, uh, there’s a discrepancy
in behavior. Or you know, the Wi-Fi behavior
is unspecified. So if it is unspecified,
perhaps we could, uh, make it work just like what–
what we described. I think that would be
probably the best solution for application developers
and carriers and everybody else
involved, yeah. man: Sure. Um, I guess,
on the YouTube app, that’s, you know,
native to the device– and as I understand it,
Apple built that, not YouTube. Um, is there some work-around
that you can envision that, if you’re building
a native iOS app, you’d be able to support
the autoplayback? Or is there kind of no–
no work-around? Wilkiewicz: So for native
iOS apps that incorporate
YouTube Video Playback, there’s really three approaches
that you can take for video embedding. One is you can kick off
the, um, YouTube player, right? So that’s one. The other one is, um, you can use the existing
Flash embed, and what iOS does is recognizes,
oh, this is a YouTube embed, and then uses a Safari plug-in
in order to play back the video. And this is where
this approach falls apart, if you build
your own Flash player and no longer can, you know– iOS recognizes the signature, and then you get a black box. And the third approach
is the, uh, iframe embed. So, um, if you build
a native iOS application, uh, you can actually use
UIWebView and embed the AS3 embed or the, uh, iframe embed, and then, uh,
iOS would recognize it. In one case it uses
the Safari plug-in. In the other cases it uses
HTML5, uh, Video Playback. The HTML5 Video Playback
has the advantage of actually having
an API around it. So I know more
and more application developers are using the iframe API
for iOS applications because it is
the only game in town. So even though, you know,
technically we’re saying that, hey,
it’s not mission-critical, but, uh, the argument
we’re making– it’s still better
than nothing, you know. So you can actually do
something. man: Yeah.
Wilkiewicz: So–so that’s kind of the story that we have. But I think all of them suffer from the same
fundamental limitation when it comes to autoplay. man: Yeah, I’ve seen
some hacky solutions where you can fire
a touch event, and then you kind of crawl the–
the player, then find out
what the play button is, then you send a touch event,
which, you know, apparently isn’t allowed
by the documentation, but they’ve let apps
kind of do it. Wilkiewicz: I see.
man: Is there any other solution beyond that
that–that works, or is that kind of the only–
only thing for autoplay using iOS?
Wilkiewicz: Uh, yeah, so we don’t have anything that
we support right now for that. man: Good. Thanks. man: Hey, so I have a habit
of putting Linux on old laptops. And then, uh,
at previous companies I was always–always trying
to get, you know, real movies
playing on old hardware. Is the performance
of HTML5 video so much better than Flash
that, like, it really makes it possible
to watch movies on old hardware? Schechter: Um, it’s– it’s sort of
a little bit tricky. So, uh, when the encodings
are in H.264, uh, the performance is generally a little bit
better than HTML5, although I don’t have
any numbers to give you. Uh, WebM–I usually don’t see
performance being as good, because, uh, you don’t have
a specialized chip for it. Um, but you know, they’re making
a lot of improvements there. Uh, but also the–
a lot of– a lot of what we’ve been
doing on WebM– uh, it’s got, uh, less bytes
for the same amount of quality. Um, and so, you know,
it’s a better– we think that it’s
a better encoding. But yeah, we’re still
sort of, uh, struggling on making sure
that the performance of, like, the CPU
is gonna be equal or better. Cool. Posnick: Okay, um,
doesn’t look like there are
any more questions. So thanks, you guys,
for coming. Schechter: Thank you.
[applause]

GraphQL at Shopify

GraphQL at Shopify


– Alright, good morning everybody. That was a really inspiring keynote. I’m also really excited
about the GraphQL Summit. We’ve seen so many talks
about why GraphQL is great, why people are using it, but we haven’t seen that many
talks on how people use it, and the problems they’ve had, and the actual solutions they found, so I’m really excited, I already talked to a few people here, and we shared problems we
encountered in our solutions, so. This talk is gonna be like, two parts. The first part, I’m gonna talk how we build the tools and solutions to some problems we had on
the server side for GraphQL. And Dylan’s gonna talk
about, on the mobile side, kind of the same thing, how we built smarter clients for mobile. So name’s Marc-Andre, I’m from the cold Montreal, Canada, and I work obviously, at Shopify. So Shopify’s an ecommerce company, we basically make it easy for anybody to sell
things on the internet, and like many companies, we’ve
had and still have REST APIs. And we had the same
pain points that maybe, a lot of you here too, so. Our clients were making
way too many round trips, to get the data they needed. They also, I’m sorry. Way too many round trips,
it was hard to customize what the other (mumbles) are getting. The way we did field filtering,
was a little awkward, so we wanted to explore something else. And a few months ago, we
played with React Native a little bit at Shopify, and although it wasn’t
perfect for our use case, we kind of discovered GraphQL through it, and fast forward to today, we’ve just released our mobile app, that’s powered entirely
by our GraphQL API. So me and Dylan, both work
on the GraphQL core team, and our goal is really to
make it easy for any team to build their own schema, or
extend their existing schemas. So, we’re getting pretty large, we have a lot of developers
working on the platform. We have many teams that might
wanna create a new schema, or extend existing schemas. And we also wanna make it
easy for mobile developers, to maybe extend the schemas too. So the first thing we tackle is, how will we define our schemas and execute GraphQL queries,
for the Shopify API. And first thing we did is, turn
to the open source community and found a great GraphQL gem,
that maybe some of you use. And we especially found a
great maintainer, Robert, so shout out to him, he’s been really helpful,
really responsive. So with that gem, it lets
you like many other (mumbles) define your types, define
your fields arguments, with a nice little DSL you can see here. But keeping in mind our
goal was to make it easy for really anybody to extend it, and we have a gigantic Rails app, a monolithic Rails app, and our developers, are used to, are really used to a
certain way of coding, and we thought, maybe
this was a little too far from what we were used too. So, what we did was
actually build a kind of two different abstractions
on top of that gem, to help us make it even easier. So we have the GraphQL
gem that we use to execute these queries, and define
the internal GraphQL types. But we also have a, what
we call GraphModel on top, which helps us define these types, with another syntax. And GraphQL, which is a layer, that really contains more of
the Shopify specifics doc. So this what our GraphModel
layer looks like. It’s quite similar, it’s class based. Types are defined using
simple symbols instead, and we have explicit
keyword arches for nulls, so it’s pretty straight forward too. And, we’re a Rail shop, people are used to ActiveRecord. You might have noticed the syntax before, kind of looked like ActiveRecord too. And we wanted to kinda push it further, and really make it easy for anybody. So we have like helper methods, like, belongs_to, which in the end
just defines a normal field, but doesn’t, you don’t
have to have knowledge, of how to define that,
(mumbles) GraphQL syntax. We have so many other
helpers, like cache_has_many, who’s gonna take care of,
fetching that from them cache for example. Or a paginated helper,
that is gonna help define a Relay connection for you. So it’s been great for helping people, just really extend our schemas, but naively building a schema using these, still result in some problems, sometimes. So let’s take a look at
this query for example. I’m gonna go through the
execution of that query, and just take a look at
what kind of calls are made, to fulfil that query. So for example here, we
might have shop here, that’s gonna select shop for our MySQL. We’re then gonna fetch products, which is also pretty simple, as we’re just fetching products, where shop_id is 1, right? But if you go down, to
the image resolver here, turns out it’s gonna
be called three times, and it’s gonna do three queries in MySQL. And that’s a little awkward, it’s not necessarily what we want. And the reason why it’s doing that, is, each resolver is actually called in different context, so maybe, if it was in the
context of one controller, obviously we would of maybe
like preloaded that data, so we don’t only do one call, but our resolvers are actually
called in different contexts, so each resolver doesn’t know, how many other images are gonna be loaded. So we could’ve went, with a solution, maybe analyzing the query, and trying to guess what
causes it to be made, so we can preload before. But it’s really complex, and maybe we didn’t need
something like that. So we went with the usual
solution of batching, and we’ve actually released the gem, called graphql-batch, which Dylan’s the main maintainer, and it’s really great, and its principles are
actually quite simple. And you might be familiar
with the principal, if you use the JS server too, this famous DataLoader, that a lot of people use, library. It’s pretty much the same principle, so the principle is to, instead of returning the value right away, resolver can actually return a Promise, for that value. And a loader for example, is gonna take the record class, maybe it’s gonna be product here, and an association, which was the image we wanted on. And, when we load for a particular record, we don’t actually fetch
the image right away, we just indicate, we’re interested in that
image at some point. So, the same kind of images before, instead of returning the
values in each resolver, we’re actually just telling our loader that we’re interested in each of these three products images. And inside an actual loader, it’s pretty simple to, we have the load method, that we just talked about. Caches, remembers and returns the Promise. And we have a perform method, that’s called only once, and will actually do the hard work, of batching that query. This example here, is our
Memcache AssociationLoader, which is gonna prefetch the images, on a certain set of products, and fulfil every Promise at once. The hard thing here, is
when to call perform. If you’ve used DataLoader,
you might know the use like, you use a little trick
that you can do in JS, they actually wait for process.(mumbles), to (mumbles) their perform, Promise, but we’re in Ruby, so we
couldn’t really do that, so what ended up happening is, we used Promise.rb to
return these Promises, which is a Promise A+
implementation in Ruby. And we use a custom executer, so basically, when we get the result, from the Ruby gem, we actually look for Promises, and resolve those. So, we actually know, that
every possible Promise was in queue. And we have a nice API for that too, so, all that logic’s
actually hidden behind a (mumbles), you can just
say, for that field :image, just please preload it, that should be field :product actually, preload images. And what that does, is basically wrapping, the normal resolve (mumbles) field, with a prefetch, (mumbles). So, before hitting the resolve, we’re actually waiting for that Promise to be fulfilled. So, that’s been really helpful, to hide a complexity behind batching, and helping really anybody, stop people from basically shooting themselves in the
foot with these queries. So if we take a look, at the same query, but with batching this time, we’re gonna have the same queries, except, this time we’re
actually gonna batch it. So this is great. We have other thing too, for authorization, authentication, is another kind of pain points, people are often hit, when using GraphQL. And one of the solution
proposes often having it, in resolver itself, checking for the user,
what they’re able to do. But we went with something
a little different. Our API clients, access tokens, already have the concept of scopes, so resources they’re allowed to access, and we actually coequated that,
with our type definitions. So for example, an order type
requires access to orders. And we can actually say, write, if you’re allowed to
write, or is it read only? Or we can even specify,
for a particular object, is the API client allowed
to access this type. And we compared that about, against an AccessControl object, that we pass in the context, and that AccessControl object, contain everything about the API client, its capabilities, what
types it can access. But there’s still, stuff that we need to
protect ourselves against, like evil clients, that might, build like really complex queries, and use many solutions,
for that query complexity, calculating a score for each field, maybe a MaxDepth, but we went with the simple solution, of a timeout, and we used just Ruby Timeouts class, which will raise, after
certain amount of time. Another thing that clients might (mumbles) like, really a lot of queries, and we’ve reused the same thing we had with our REST APIs, which is simple Throttle. It’s got a leaky bucket throw, which will raise to an API client, for a certain key, and the key might be a, a shop ID in our API client, we’re gonna Throttle it. So that’s helped us, mitigate
some of those bad clients. But really, the cool thing, that I really like about
our Shopify (mumbles) is really the developer experience, around building and extending schemas. So, we always checking
the IDL of the schema, in version control, so we can actually really see easily, what’s been changed on a
schema, breaking changes, and we enforce that. So if we compare the new
schema against the old schema, and if you change the
type, change the field, we’re gonna ask to
rebuild that schema_dump and check-in. And pay really careful attention, to not break the schema for no reason, so when you try to generate the new dump, if something has changed, and it’s creating a breaking change, we can detect that, and instruct, either you
wanna really wanna break it, or maybe rethink what you just did. So, we also have on GitHub, we have a bot that’s gonna
alert our GraphQL team, whenever the schema changes. That also helps us, maybe
help someone is new to GraphQL that might have just added a field. And we also have a strong of conventions, these are just a few examples. Our mutation actions, we always prefix with the object name
instead of the action. This is makes it a lot easier
for us to find mutations, since we have a lot. Our mutations return a userErrors, for errors that should
be shown to the clients, and we support the Relay spec always so, we have actually a test
that will check for these, for example, did you forget
the client mutation ID for a mutation? We might instruct you to rename mutations, and we have a few other things to be like instrumentation. Instrumentation is really
important for us at Shopify, and GraphQL wasn’t different, so we currently have any errors, that (mumbles) we have. Query time, clients that were troubled, or time outted. We really wanna have, like more precise instrumentation, so we’re working with Robert, the author of the GraphQL gem. So we can have maybe nicer hooks, to instrument actual resolver times, and things like that. So, we also have deprecated
field usage tracking, so every time a deprecated
field is executed, we actually log it, and we’re able to using a tool, a little bit like log stash,
that we use internally. We’re able to actually query for these, deprecate fields, and makes it really easy to know when you can safely remove a
deprecate field, or not. So that was a subset of
what we have currently, on server-side, and there’s so much more, so feel free to come talk to us after, but for now, I’m gonna let Dylan come talk about what we did, with
our mobile clients. So please welcome Dylan. (audience applause) – Alright, so, first I’m gonna give some background on what
we’re actually building, and help you understand
why we built what we built, and how we got there. I’ll talk about how we
use code generation, in order to build the
clients that we built. And also talk about, about how we do caching, and more importantly, how do we keep data
consistent across the app, when it changes. We were rebuilding the mobile Shopify app. We were told, we needed to rebuild it, so it could provide a full
Shopify Admin experience, rather than a limited companion app. So we decided to, make sure that it was also
fast, on slower networks, so we changed the text stack as well, and we looked at GraphQL as a way of reducing the response sizes. We managed to ship it,
in time for our deadline, which for us was, because we’d wanted our
merchants to have it before they were busy
with the holiday season. We started by building it in React-Native, we found it very quick
to build out the app, but we had some troubles with making sure we could polish it, and get it to the quality that we wanted. So we ended up, deciding that, since this was a mission critical app, and we weren’t sure if we could get the quality we wanted,
in the time we wanted, we ended up going with
native mobile (mumbles). So that meant writing Java and Swift, and also meant, we had
to leave behind relay, which is in JavaScript, unfortunately. It was a hard decision, but, let’s see what type of goals we had with building the new GraphQL clients. We wanted to keep it simple. At this point, I’d already worked on building out the server side, so we had something there, but we needed something
on the client to query it, we didn’t just want to specify, literal strings, which would be ugly, or deal with the raw JSON directly. We also wanted it to
be easy to understand, for our mobile developers. They’re the targets, they’re
the ones actually using this. So we want to embrace with their use too, which is a static type system, and IDEs. So we wanted to fit in well with that, and we want to think of
what’s going to happen, after we actually ship the app. We need to make sure we
can, evolve the schema, and we want to be able
to add to this over time. This initial release is just the point where we can replace the
companion app we had before. And for our (mumbles),
decoupling the code, so using components so we can
easily add components to it. In order to do all this, we realised that a GraphQL Schema was the killer feature for this. This is what, I think Facebook really developed this schema for, they wanted it to integrate
well, with static type system. So we use that for code generation, we built code for building a query, and for the response classes. And we want to make it easy to use, so we have a script that doesn’t require any arguments to use, and that only has to be around
in order to take advantage of anything added to the server. So let’s look at what the code
generator produces for us, starting with the query builders. On the left you can see the code, for building the query, and on the right, the
query it actually produces. Notice the similarity between that. That’s on purpose. We use the builder
pattern, to implement this, where we add fields by calling methods, which are (mumbles) by the returning the object they’re called on. And we use closures, to specify, subfields on those. We use literal values, in order to specify any arguments, which are provided when
we’re building the query. So the query is actually
built (mumbles) time. We have a similar API for Java, and at the time, we thought it was going to be hard to provide this because, Java 8 had lambda expressions, but there was no support
for that on Android. But we found out we could use retrolambda, to backport this to
older versions of Java. Now there’s an Android toolchain, which might adopt when it’s
more mature, called Jack, which also provides support for this. Normally, it seems like
fragents are used for composing queries, letting components specify what they want, so they can keep their data dependencies close to their data use. We took a much simpler approach. Instead, we just have a
function that takes in, query builder object, and we can put that on the component, and it can specify what it wants. And we don’t have to have
anymore support for it, or explain anything to our developers, because they know how
to do function calls. We do use fragments, inline fragments, in order to specify what fields we want, on a specific object type, when we’re dealing with an interface. In this case, an event is an interface, and type name is automatically
querying on that, which we’ll use in the response classes. We also automatically query IDE, which you’ll see that we use in the data consistency layer. By generating these type
specific query builder classes, and methods for each field, we integrate well with the IDE, because we can actually check these, at compile time, by
just compiling the code, and the IDEs since can
check for compiler errors, can highlight any errors for us. We also get auto-completion for free, and by generating documentation, we can surface that in the IDE. We also have deprecated field use, by using annotations for that. So we kept things simple, by leaving out some GraphQL features, and by intergrating with the type system, we were also able to get
all the IDE features, without having to implement an IDE plugin, just to provide that. We also were still able to do the loose coupling that we wanted, even without these features, and as the schema changes,
we just run a script, and we can adapt to that. And notibly, in order
to remove the fields, since we evolve the schema, we can just mark them as deprecated, and when they run that script, they’ll get some warnings
for deprecated field use, and can easily address them. Let’s look at the response classes, because we don’t wanna deal with raw JSON. Instead, what we get to deal with, is something much more pleasant. These response objects, are already validated
when they’re constructed. They all deserialize the fields, and provide access or methods, so that we can leverage the type system, notably dealing with nulls is a common pain point, so we use Swift optional types, which allow us to, be
forced to check for null, where necessary, for nullable fields. And for non null fields, we
don’t have to be concerned with can this thing be null or not, that’s all encoded in the scheme for us. We found this so convenient, we decided to replace the model error, and for this app it made sense. We wanted to keep as much logic
as we could, on the server, and that way, we didn’t
have to worry about avoiding code duplication. And notably we can leverage, all that code that’s already
written on the server and allow it to change over time. Since we wanted our response
objects to be convenienced, that also meant looking at custom scalars. We didn’t want to deal with,
the string as it’s encoded in the JSON response, where
money or daytime types. So instead we took, approach of keeping our GraphQL
code generator generic, so it’ll work on any schema, but providing a way for
it to be used, where custom scalars can be, we can pass them something, that can specify how to
generate code for it. So in this case, we specify what types to actually use for it, and how to serialize and deserialize it. So our update schema
script is what adapts, to our specific case, for that application, whereas the code generator
is separate from that, and ideally we can open
source it in the future. Another thing we have to address is, the fact that we don’t know all the types that are ever going to be there. We generate code, and we’re
going to ship the app, and people are going to
run with all the versions. So that means you have to
deal with the known values, that we don’t know about
when the code is generated. To address that, we
generate an unknown value, and add it to the unknown
type, for the language, like Swift or Java. And, in the case of
exhaustive switch statements, we can be forced to actually
have a case for that, as well as being forced to
handle all the known cases if we choose to, use an
exhaustive switch statement. For interface fields, we return the object type, for the actual object, and make sure that it
implements the interface. But that means using the type name, and making sure that, we
can actually match that, with a corresponding response class. So we have to deal with
the unknown case again. So we generate an unknown
class, for each interface type, so in this example for events, we have unknown events class, and that implements the event interface, and that way we can fall back
to using the common methods, on common fields on, that interface. So in summary for the response classes, we simplified things, by avoiding a hand written model layer. We integrated well with the type system, and we left room for schema evolution. And in addition to that, we left room for the
business logic to change, without having inaccuracies, by just being able to
add fields to the schema, that are computed fields. And we use that liberally, to keep code, that we’ve classified as
business logic out of the client. So, let’s end by talking
about how we cache things, and keep data consistent across the app. For caching, we cache whole responses, and we key that based on the query string, and persist that to disk. That way, when the app is reopened, we’re able to quickly render something, and if that data’s changed, we still actually send that query, so we can get back the
response and change the view, with the actual data
that we’ve just gotten. So we have to handle
both of those responses, and we have a handle relay query method, that’s defined on a relayable object, which is an interface. And we pass that relayable object, to the method query, for the data. So that handler method, would
get called multiple times. And we also keep that
relayable object around, in a list of active queries, which represent all the parts of the app that’ll need to be,
updated when data changes, And we have a weak reference, in order to clean up that
list of active queries, when anything in it, no longer is there to update
when the views gone away. When a mutation happens, we wanna update the whole app, so that we can see that
change represented elsewhere, and even as they navigate through the app. So what we do, is we find all the nodes, on the mutation payload, and match that against nodes
in the active query objects. In order to find everything
that needs to be updated, and we match those by
just comparing the IDs. We deep merge the fields
from the payload node, onto the active query node, and then call that
handleRelayQuery method, in order to update the view. Optimistic updates are similar, in that they target a specific node, and specify what action to perform on it, like deleting it, or
we might do an update. For the updates, we
use the response class, in order to specify that, and have setter methods, so we can specify what changes on those. When the response actually comes back, we wanna rollback the optimistic update, because we don’t wanna assume
that that actually happened, it’s just a present what
we think will happen, before the actual response comes back. So we kept things simple, we just had the whole response caching, and we use type checking,
for optimistic updates, and I think we’ve built
something for the long term, by making sure that we
have data consistency, without coupling, which means, when we do a mutation, we don’t say, here’s all the things that needs update, we just do the mutation, and
let the updates propagate. If this talk has been interesting to you, you can follow our work
on our engineering blog, and as pointed out that, some people are hiring GraphQL devs and, I could certainly use some help, and hopefully I can
open source some things we’ve been working on here. At least the generic parts of it. Thank you. (audience applause)

Transport Tracker Solution for Google Maps – Geocasts

Transport Tracker Solution for Google Maps – Geocasts


SPEAKER: Google Maps
offers you a wealth of data for building
location-aware apps. Let’s take a look at a solution
for location-aware apps, a transport tracker. This gives you a
complete solution for understanding where your
various vehicles, such as buses or trucks, are at
any given time. What does a system for
transport tracking look like? Well, first of all, there
are the vehicles themselves. You need to understand how
to report on their location, how to store their
location in a database, and get real-time updates
from that database. How to display the
vehicle locations on a map, how to style
that map, and how to administer the whole system. The Transport Tracker
solution will teach you how to build all of this,
including the map where all of your assets
are, a mobile app that reports on the location
of the vehicles, and the back end that
ties everything together. In particular, the back end
receives real-time updates of the vehicle locations and
sets up the display panels for use by the front end. You’ll learn through
tutorials and documentation, with a full reference
implementation in open source. With this, you’ll have
the basis for building any kind of asset-tracking
solution using the full suite of tools
available for Google Maps. Learn more at developers.googl
e.com/maps/solutions.

Web Development – CSS3 Transform, Transition and Animation

Web Development – CSS3 Transform, Transition and Animation


Web Development CSS3 Transform, Transition and Animation Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Don’t Forget To Subscribe Our Channel Thanks For Watching

How to spin products in 360 degrees in Shopify

How to spin products in 360 degrees in Shopify


Want your Shopify product images to stand out? Well there’s an app for that! Magic 360 How can I try it? It’s easy… Go to apps.shopify.com/magic-360 Click ‘Get App’ Enter your shop name. Click ‘Log In’ Click ‘Install app’ to install Magic 360 on your website Simple! Magic 360 is now installed What next? Check your images are ready Your images need to be numbered in the order you want them to appear on the Shopify product page. Eg: 01, 02, 03 etc Use large versions of your images for spin, magnify & full-screen effects. Shopify will resize images automatically throughout website Your images should turn ever so slightly to create the 360 degree spin First, enter number of images you will use on Magic 360 Settings page To get to the Magic 360 Settings page from your Dashboard… Go to ‘Apps’ Select ‘Magic 360’ installed app You’re now on the Magic 360 Settings page… Go ahead and enter number of images Go to ‘Number of images on X-axis’ Enter number of images used to create 360 degree spin This example uses 36 images Number of images on Y-axis: 1=spins left/right only Enter number of positions spin can move up/down Next, upload 360 spin images to product Go to your product page Click ‘Choose images’ 1. Highlight images you want to upload; 2. Click ‘Open’ You will see your images uploading Once all images uploaded, click ‘Save’ Go to website and refresh product page Your 360 images are now showing on your product page Because you have uploaded large versions of your images, you can spin… magnify… and enlarge to full-screen It takes seconds to customize Magic 360 Choose from over 30 customizations Completely customize Magic 360 how you want Image size, magnifier shape, message + much more… Bring your images to life! You’ve got nothing to lose… And everything to gain Get your free trial here: http://apps.shopify.com/magic-360

JavaScript achievements of the Year 2019 and key Frontend’s trends: FrontEnd news (25 Dec’19)

JavaScript achievements of the Year 2019 and key Frontend’s trends: FrontEnd news (25 Dec’19)


Hi! Let’s talk about JavaScript achievements of
the Year 2019 and key Frontend trends. I’m Anatol, and you are watching the Good
Parts of the Frontend development. If this is your first time here and you want
to learn Frontend and JavaScript, start right now by subscribing, and don’t forget to turn
on the subtitles. [Music playing] Let’s start with the exciting news! According to “GitHub octoverce” and RedMonk’s
Programming Language Ranking, JavaScript is still the #1 programming language! TypeScript is also rocketing up and now sits
at position #12. Bye the way, for the first time, Python has
overtaken Java to take second place. There are several features approved to be
part of the ES2019 specification. Among them are “Array.flatMap”, “String.trimStart”,
and others. JavaScript, as a language, demonstrates continuous
growth. One more feature that is expected to be shipped
in JavaScript is “public and private class fields.” It’s already available in Chrome and Node
and might be delivered to other browsers soon. The project Deno made progress. Therefore, Ryan Dahl presented how to get
started with it. Deno is a safe, secure and reliable alternative
to Node; thus, you might be interested in it. [Music playing] React hooks have been presented as a complementation
to the React context. That might be a game-changer because we can
omit state management libraries (like Redux), and React becomes more framework-ish. Probably, some next year, we will see “react-router”
or “Styled components” to become a part of React. We’ll see. During “Google I/O 2019,” it has been announced
that Googlebot now runs the latest Chromium rendering engine to ensure support for the
newest web platform features. It means that SEO is now becoming much more
comfortable. One more great feature is WebAuthn, password-free
logins API on the web approved by W3C. It is already available in Android, Windows
10, Chrome, Firefox, and Edge. The OpenJS Foundation welcomes two new projects:
AMP and nvm. These are the first projects that are engaged
by the incubation process since the Node.js Foundation and JSF merger! I believe that’s just the beginning. World Wide Web Consortium (W3C) brings a new
language to the Web. Following HTML, CSS and JavaScript, WebAssembly
becomes the fourth language for the Web which allows code to run in the browser. That might change the Web in the following
years. We’ll see. That’s all in my list. What’s about you? In your opinion, what’s the most influenced
on the Frontend this year? Leave your comment down below. [Music playing] If you like this video, give it “thumbs up,”
share it with your friends, subscribe to the channel and watch other episodes. Thanks for watching and Happy New Year!

Jeff Conniff and Tilo Mitra: The Future of YUI CSS

Jeff Conniff and Tilo Mitra: The Future of YUI CSS


Jenny Han Donnelly: And we’re back. Next
up we have Jeff Conniff, who is our resident visual design and interaction design guru
come engineer, walking us through some of the CSS related changes that we have in store,
followed by Tilo Mitra, one of our newest team members who is a former intern as Dav
mentioned, talking about some of the exciting, again, CSS related projects that he is working
on. Jeff Conniff: Thank you Jenny. [applause] Thank you. I’m glad to be here today and
get an opportunity to talk about skinning. I’ve been asked to give a brief bio just
at the beginning. I would like to mention that my lifelong quest for all things visual
and beautiful started very early in my life. My mother was an artist, and she prompted
me. Got my very first opportunity to exhibit a huge full-scale full-color mural. It was
beautiful. Unfortunately it was on the wall of my hallway to my parents’ room. It was
called Exploration in Crayola on Latex Enamel. [laughter] I learned one thing very early on, that there
are critics everywhere. [laughter] It was demanded that I scrub off every one
of those brilliant strokes. Even though I was probably damaged psychologically,
I still went on to get a degree in graphic design and have spent 15+ years ñ I think
that’s the right euphemism, 15+ years ñ doing visual design, interaction design, and
prototyping, and found myself on the YUI team. I’m the author of the dial widget and the
night skin and lots of fun library examples that you may have seen. I’m right now currently working on skinning
with some really brilliant and fun people to work with. I’d like to mention Matt Sweeney
ñ you want to wave your hand back there, Matt? The great Matt Sweeney. [applause] And Anthony Pipkin, is he here? Yep, there’s
Anthony over there. Great guys. [applause] Having a lot of fun. In the animal kingdom, it’s a very critical
skill to be able to blend in to one’s background. So that’s critical for two reasons: these
critters need to be able to blend in and hunt well so that they can eat. That’s critical.
They also need to, for the second reason, they need to be able to hide well so that
they’re not eaten. Those two things. It’s similar in UI skinning, but it’s also a little
bit different. It’s not so much an eat or be eaten proposal, but you really need to
blend in and look like you belong so that you don’t have the sore thumb effect happening
when you’re putting a widget into a UI. Fortunately in YUI we’ve got this great system
for skinning the appearance of things, pages and our widgets. It goes something like this.
It’s very easy. Just in the body tag or anywhere else, other DOM element, you can put in a
skin with theÖ I’m sorry, a class name with the skin there. And then in this case you
don’t need it because sam skin is the default, but in other cases you could put in a filter
that has that same matching name. And that’s all it takes to get the skins and interchange
them. So we have two skins today. One is called
the sam skin, which works great for light backgrounds, and the other one named, for
obvious reasons, the night skin. So we have those two skins today. The only problem with
that is that if you’re working in some sort of application or web page that looks like
this and you want to put, say, a tabview control in that open spot to the left of the cinnamon
roll, so you drop that in, the colors don’t quite cut it. They’re not just right. So
what are you going to do? Well, you’ll have to do some tweaking on that. It becomes really
apparent to us that we need a lot more skins. What are we going to do to get a lot more
skins? Well, the process unfortunately is a little bit tedious. You have to understand
a lot about each of the widgets, or at least the widget that you’re going to put in your
app. You need to look at the CSS and understand where the classes go and where they fall on
the different DOM objects. You need to connect all the dots and put the right values in the
right place to make them look correct for what you’re doing. Now that’s great for one widget, but if you’re
trying to do a skin for the entire library of all of our widgets, that’s a bigger job.
You’re really doing similar work times n widgets. It also helps to have some experience
with visual things and color, and to have spent some time doing some of that is a helpful
thing. Even with that and the patience required to do that, it’s still a little bit like
the process of manually creating the inner works of a wrist watch one at a time. It’s
a lot of work. We tried to figure out what we were going
to do about that, and we looked around at some other people who were trying to solve
the same problem and saw a couple of solutions out there. One of which is a solution where
you look at every individual possible style-able element in your user interface and you give
it a very explicit variable name, and then you provide an input field for whoever’s
going to do this skin for them to enter in their own hex value or their font or their
size of padding or whatever it is. So you’ve got a lot of input fields and a lot of variables.
You can imagine that each time you add something to that space it just gets longer and longer,
so it becomes kind of unwieldy as you can see. That may share the work of producing
skins, but it doesn’t necessarily make it a lot easier. Now, an obvious answer to that would be to
group the variables together and have multiple things point to the same variable so you don’t
have quite so many buckets to deal with, quite so many inputs to fill in the values for.
We saw some of those and you can imagine that you might come up with some names for UI groups
of things that could be commonly shared, like title, or header, and you could color them
this way. But you might find that there are some things that might be a little surprising.
It might be somewhat expected that you’d see that color for title and header behind
a tabview, but on the other hand you might be surprised when you find out that that same
color is also being shared by the range between thumbs in a slider, or maybe even more surprised
to find out that that same color is being used by a progress bar while you’re waiting
for something to load. So you’re having some problems with unexpected consequences from
combining those things together. Another example: you might think a good generic
value to set variable would be for something called clickable. We use a general name, so
you’d say great, I’ll color my buttons dominant because they need to be seen a lot, so I’ll
give them this color. I might not realize that spin controls will also have that dominant
color on those buttons, and I might not realize that the thumbs on sliders are going to have
that same dominant color. You might have expected that, but you might not. What you might really
not expect is that every day in a calendar since it’s clickable, guess what? Gets the
same dominant color. Really an unexpected consequence. In some of these systems you can select and
change every single little piece and it’s just a lot of work. That doesn’t assure that
you get what you want. That reminds me of a story about work. There once was a neighbor
down the street. His name was Bob. Bob lived in a nice home with his wife Sally. Sally
was always after Bob, saying Bob, I think it’s time that you painted the house, Bob,
when are we going to paint the house, Bob we haven’t painted the house in 12 years.
So Bob finally decided that while Sally was going back east to visit family for a couple
of weeks, he’d surprise her and he’d paint the house. So while she was gone, that’s what he did.
He got all the chips. Fortunately Bob doesn’t live in an area where there’s a home owners
association, so he really had total freedom to do whatever he wanted. He could pick any
color, which was great news for Bob because he really loves color. So he has a house that
has a lot of individual pieces that he’s got to decide exactly the relationships and
these colors. He got to work and planned it all out, and he did paint the house while
Sally was gone. On her return, Sally was indeed surprised to see that Bob had painted the
house. Just the fact that Bob had the ability to paint every single piece any way he wanted
to didn’t mean that he was going to get the good relationships and get a good paint job,
and Sally would agree with that too. That brings me to another issue, just like
in UI this is a problem. There’s another principle that I’ve called the whac-a-mole
principle. You all know the game, the mole pops up and your hammer him down, and as soon
as you hammer him down he pops up somewhere else and you hammer him down and it goes over
and over and seems endless that you’re trying to get this thing to stay down. Well, the
same can be true when you’re picking colors and setting things together. Let me show you that. To demonstrate that
pain point I’d like to use my acupuncture diagram. This is a simple example of a widget,
just a menu. It’s sitting on a white background. Say you were going to change this because
you realized that you needed something that was going to be on a green background and
it just didn’t look quite right. You’d think, well, I’d just make it green, no big deal.
Maybe you’ve thought this. But once you got into it you thought well, I’ve got to change
this body of the bottom part to something green, and if I do that then this highlighted
section doesn’t look good so I need to change that. By the way, this blue text on the green
background doesn’t look very good, so I’ll need to change that. And by the way, the border
doesn’t look very good, so I’ll need to change that to green, maybe a little darker.
And this top tab really needs some work too, it can’t stay blue. Then its text of course
can’t be blue, it needs to be green. And the border also has to be green. We haven’t
even gotten into the hover problems ñ you know, if there’s hover and a lot of other
colors have to be selected. Then you step back and you look at it, and
this is the whac-a-mole part, and you say that’s good except I think the body needs
to be a little darker in that menu. So as soon as you change the body color to be something
a little darker, then the text is not readable, so then you have to go back and do the text.
I think this conference only goes for a couple of daysÖ [laughter] Öso I probably can’t really show the full
effect of whac-a-mole. But that gives you an idea. This is what we did, or actually to be more
accurate, what we’re doing. We started with two skins like I mentioned, and we did a little
research and we decided that we would probably need a number of skins that would be pretty
close to the number of unique visual color schemes and designs of apps and webpages on
the internet. So we did some calculations and did some research and found that we’d
need about 29 million. [laughter] Question? Audience member: How did you get this number? Jeff: The question is how did I get that number.
Well, we actually employed some of the same research methods that politicians use frequently. [laughter] I got that off my RSA token. But you can use
any random number generally. [applause] And it seemed plausible so I put it in. [laughter] Thought you’d be fooled by it, but I guess
not. At any rate, we need a lot. Unfortunately people who have the time and the patience
and some of the skills to put these together is a relatively small number, so it’s going
to take a long time to get to 29 million skins. What we had to do was figure out a way to
eliminate a lot of those special skills and a lot of the tedium, and that’s exactly what
we’ve done and are doing. We needed to make this easy. How easy do we need to make it?
Well we need to make it easy enough so that just about anyone can create a skin or their
own skin whenever they need to in a short amount of time. So how to do that? Well we looked around and
the other solutions that we were seeing didn’t seem to be the way we wanted to go, and we
had a different idea so we’re proceeding with that in a different direction. From our
experience in working with color and design we’ve learned that colors are a lot like
people. You could really group all people in the world into three categories. Everyone
knew that, right? [laughter] Three main categories. There are the ones
that seem to always need attention. They always have to be in the limelight, they always have
to be out there and people have to be looking at them. And then there’s the other group
that’s not quite so in your face. You know, they’re important and they have a purpose
but they’re not always out there shouting. And then there’s a third category that seemed
to fade into the background and you don’t really even know they’re there, but they’re
still necessary. The same is true of colors and UI elements.
To give an example of that I’ve put together a test, and don’t worry, this is not graded
but we’ll all be taking this test together. You don’t have to hand in your papers or
put your names on them. In fact this test is made ridiculously easy just to make a point.
I’m going to show you five blocks of color ranging in emphasis from most emphasized to
least emphasized and see if you can pick out which one is which. Grade them from really
dominant to really recessive. Do you need more time? [laughter] Did I make this too easy by stacking them
from brightest to darkest? Maybe I did, okay. All right, so that’s throwing it off. I’ll
mix them up a little bit. What do you think? Need more time? Maybe I made it too easy because they’re
all blues. Maybe if I mixed in some other hues in there it would be more difficult,
so we’ll see. What do you think? Pretty tough? No. Got it. Pretty easy to do. Well, it’s really relationships of colors
and which ones are emphasized and which ones are not. You can take this kind of process
and apply it to text elements as well, or objects on a widget, or on a UI. That’s what
we’ve done, is to create instead of buckets of UI named things to attach skins to, we’ve
created a hierarchy of emphasized color values and then we point the widgets at those generic
values. Let me demonstrate that. You’ll see something
that looks kind of similar to what we saw in the test. We’ve got some boxes there,
they’re labeled, but they’re going from most emphasized item to least. Don’t be fooled
by the fact that the least emphasized one is a wrapper for the others. So what can you
do with this? You can select the key color ñ in other words you don’t have to change
all the color blocks, just pick a key color, let’s say green, and you automatically get
colors of green that are in different emphasis. Really all you’re doing is picking one color.
Want red? Got the same thing. Want a dull red, a dull dark red? So it’s as easy as
using a color picker. You’re establishing this relationship of colors and color buckets. Now there are not just four colors to choose
from, but we’ve really got a whole fully fleshed out set of text of different weights
and gradients and rules and borders in the system. Selecting anything in there changes
everything just at a single click. The second thing that you can do to change
this scheme of emphasis is pick a color scheme. A color scheme is really nothing more than
how those main blocks of color bucket relate to one another. Right now we’re looking at
monochrome, but if you really wanted just a single color as your highest emphasized
color and the rest gray, you could pick the color plus gray color scheme. Then the same
thing happens wherever you go. If you wanted a complementary color scheme
you can do that too. You pick green and you get the complementary color of that. If you
want a dark background. And the last choice down there and more means there can be infinite
number of color schemes. We’re obviously going to start with more than just the four
or five, but you can see very quickly that if you have say 16 million colors in that
color picker and you throw half of them away and you multiply that by even 4, you’re at
29 million possibilities right there. Let’s go back. Whenever you’re trying to
take something visual and aesthetic and you’re trying to create it by math, you run into
some thorny problems because one and two don’t always equal three when you’re dealing with
things like color. There are some problems that we run into. Fortunately Tony Pipkin
ñ if you can stand up again Tony, or wave or something ñ he’s my super hero. I had
cobbled together some code that was sort of doing this, but Tony came in and rewrote some
great methods that are now part of the library. They’re Y.Color, and he’s got some great
things in there that take care of these thorny problems. I’ll let you applaud after you’ve
seen some of those. Let me show you some of those. First is readability.
You can imagine any time you’re changing background colors you’re going to quickly
have problems with readability of text sitting on top of it. For example, this slider changes
only the background color, and you can see if you start doing that you’re going to get
into some things that are pretty hard to read real fast. But fortunately thanks to Tony
and Y.Color, we’ve got getOffset, which really just takes a background color which is the
background hex color there, and offset lightness value, it could also be offset saturation
or hue, and in this case it’s 20 per cent lighter, and now anywhere I move the slider
you can see the text is readable. Which is pretty nice, except when you start getting
toward the top, right? For that situation we’ve got just another minor piece of code
that flips the sign of 20 so that it stays readable no matter where you move. So that’s
getOffset in Y.Color, which was really nice for me to use in this. The next thorny problem is introduced by this
humble gray rectangle who has no color at all. He has no hue. I’m sorry, he does have
hue, but you won’t know it because it has saturation of zero, so he’s plain. He’s
only 50 per cent brightness ñ I mean if he were 100 he’d be white, if he was zero he’d
be black. He’s pretty boring. But a much more festive rectangle is this one. It has
all of the hues from left to right. They’re all 100 per cent saturated, and make special
note that they all have the exact same lightness value of 50 per cent. Now if you squint your eyes and look at that
bottom thing you might see that some of them seem to not be 50 per cent, some of them seem
to be a little darker. In fact if you take that color band and you turn it into a grayscale
image in Photoshop you get something like this, which makes it really obvious that some
of those colors are not appearing to the eyeball to be the same brightness. Why does that matter? Why am I talking about
this? Well if you took two colors out of the color spectrum and say you wanted to use those
two colors in a widget somehow and you had two objects that you wanted to have sitting
next to each other and you wanted to have this system that can change hues to anything
the user wants but you want those two elements to keep the same relative dominance, when
you ran into a combination like yellow and blue one is going to scream and the other’s
going to be quiet. So that’s a problem of brightness. Fortunately we have another thing from Y.Color
which is called getBrightness. This is no getBrightness method happening now, but you
can see as I choose different colors that the comparative lightness between the purple
on the left and say the blue on the right is very different. But in Y.Color now there’s
getSimilarBrightness. Now when I pick a green, and you can see that it gets input on the
first argument, and I tell it to match the lightness of the color on the left, that I
get a different value for green that’s equal in brightness. You can kind of see how this
works out. As we pick different colors around here for the most part, monitorsÖ You can
see we’re maintaining the brightness. Also in Y.Color is a harmony utility, which
in this case gives us getComplementary color. Now when I select say green for instance,
it’s the color on the right, it’s going to give me the complement of green which is
red. Then we use getSimilarBrightness to take that red color and say give us a red that’s
the same lightness as green, and we get this result here. If I pick red then it takes red
and gives me green, and then tones down the green so that it matches the brightness of
the red. These are really nice utilities and I really appreciate Tony’s work on that.
Let’s give him a round of applause. [applause] Those thorny issues are no longer thorny.
If you’d like to look at Y.Color that’s a link to it if you get this presentation. Now that I have the ability to work in that
color space and have confidence that I’m going to get these benefits from Y.Color,
I can get widgets that are hooked up to that color space in intelligent ways so every element
in the widget can then point to its right level of emphasis. Then no matter where I
drive the colors, that emphasis is kept. Let me demonstrate that. If we look for a green, one click and we’ve
got a green monochrome scheme. Pick a different color scheme, maybe complementary, and we
have a green and red color scheme. Pick something over here. Saturated, low saturated, anywhere
you go. You can see that these widgets are all behaving the way you’d expect color-wise. These are real widgets, by the way, this is
not smoke and mirrors. This is the real stuff. We’ve got hover colors also taken care of.
I didn’t have to set any of those at all. All the widgets are now hooked up to this
extended color space that looks like that, and it all behaves well. We’re also doing
a little experimenting with border radius, if you watch the radius on the buttons. And
also padding. These things are very easy to manipulate without having to dig in and set
individual variables everywhere. So that’s how we’re doing that. Since you’re
here, you fit the demographic of people who the first thing you do when you get a new
pen isÖ what? Take it apart, right? So let me show you a little bit about how the color
space is connected up to the widgets. I should remember to mention that you only have to
do this once, and we’ll be doing this before it’s even out there. This, that I’m about
to show you, is not something that has to be done every time you do a skin, it really
is just as easy as picking a color scheme and a color. This is under the hood. Let’s start with a single individual element.
Let’s take the text on a highlighted tab of a tabview widget. Where does that come
from? That comes, of course, from the CSS, but how did that hex value get there? Well
that came because there was a variable that was replaced in a template, and that variable
came from a mapping file where that variable’s mapped to the correctly emphasized place in
that color space that we looked at. That space.block.highest.text.normal is a location in a JSON object, an element
in a JSON object that represents the entire color space. You have space.block.highest.text.normal,
and that’s where the hex value is. Now how did that hex value get there? Well
this whole color space JSON object is represented in the UI that we’ve been looking at. It’s
just the visualization of the color space. That element for that particular string of
text came when we clicked on the bucket for the highest key color and we chose a color
from the color picker. Then smart calculations happen and we got all the color space filled
out with all of its values. So that’s how it’s put together. In summary, we should avoid the hard work.
We should avoid defining everything and the whac-a-mole pain. We’ve taken a different
approach – this is relative levels of emphasis. CSS variables to point to this color space,
and again, this is one time work that we’re taking care of. Under the hood it’s Y.Color.
And really all you’ll have to do is pick a key color and a color scheme and you’ll
have millions of possible skins. Thank you. [applause] I think we have a microphone coming for you. Audience member: Hi. So how is the impact
of this dynamic mechanism effect on the performance? Jeff: Actually all of what you’ve seen will
be put in place just to generate a skin, and then that skin will be used the same way skins
are today. You’ll just reference that and it will automatically load the CSS file for
those. Really, performance is none. Audience member: Okay, second question. Does
this use any less? Jeff: Actually it can use anything to substitute
the value in that mapping file. We’re going to be publishing this and getting feedback
to find out exactly what the right system is for that. Audience member: Thank you. Audience member: Hello. Oh, sorry. Do you
feel like the automation of this constricts your ability to maybe fine tune some of the
details on something, and what are the drawbacks to that? Jeff: No actually, because you can pick any
color to start with as a key color, and you can easilyÖ We’ll be making it so that you
can create your own color scheme, which is the relationship of those four blocks. You
have a lot of flexibility. And then at the end of the day this is just generating a CSS
file that today you would have to go in and edit anyway. We’re not restricting that point.
Then later on we can probably add the ability to tweak individual things. Matt, speak up if you want to add anything
to what I’m saying. Okay. Audience member: Just curious what sort of
timeline you’re looking at for when this will be included in the public release and
all that? Jeff: Actually what you saw today was working
code. We’re going to polish that up just a little bit and make the tool that I have
there that I showed available so that you can create skins sometime in the near future.
Then we’re going to properly format the code and put it actually as part of the library.
Did you want to add anything on that Matt? Matt Sweeney: Yeah, the longer term strategy
is to incorporate it into the build process and stuff, the shorter term is get the skin
generator out there so people can create style sheets and contribute those. Jeff: Yeah, so we can get feedback on that
as well. Audience member: So just to be clear that
if you’re writing a gallery module, say, then before too long you’ll write a CSS template
that will refer to that color space rather than CSS? Jeff: Right, right, once. Audience member: Thank you from the bottom
of my heart. [laughter] Jeff: You’re welcome. And that’ll be once,
you’ll only have to do that one time. Audience member: Hi. Do you have any concept
of a lightness or darkness threshold to where certain elements gain or lose styling? For
example a drop shadow may look good in a medium brightness skin but maybe on a light skin
it might not look quite so right. Do you have anything like that in your skin builder? Jeff: A lot of theÖ Are you asking specifically
about drop shadows, or any color element? Audience member: It would be any color element,
or you know, thickness of borders or things like that. Jeff: We’re looking at thickness of border
too, so we’ll be anxious to hear community feedback on thickness of border. There is
a lot of code in there that handles the relationships between all of those colors and makes sure
that they don’t fall off the radar and you can’t see this part. So yes, we’re handling
that. There’s a lot of really good stuff in Y.Color that’s helping us write smart
code for that. Audience member: Hi. I’m wondering how you
handle spacing between buttonsÖ Over here. If you thought about automatically calculating
the proper spacing between buttons? I find that we have several modals in our application
or other elements on the page, because we don’t have a consistent way of easily padding
between different objects. Are you guys thinking of that too? Jeff: That’s good input, and I’ll make sure
that we think about that, get that in there. We have been working on padding within widgets,
within objects, and that’s good input. We’ll take that as padding between objects. Good.
I mean margins between objects. Audience member: Over here. Will it be possible
to generate your own color combinations? Like say when it generates CSS you canÖ Like you
have the opposite colors and stuff like that. So you generate your own kind of library that
will generate CSS for you? Jeff: I’m not sure I understand. Did you
mean a different color scheme? Audience member: Yeah, you know like you have
the complementary colors, maybe you create your own that will generate CSS for you. Like
you modify the existing library. Jeff: Right. The whole color scheme picker
that I had, the radio buttons on the top left were choices between how the individual major
blocks of color relate to each other. You saw complementary in there, and monochrome,
and color plus gray. It’s wide open, and we’re using all of the Y.Color utilities
for that so you could have triads, you could have split chromatic things. So anything is
open to that. The Y.Color has a whole full library of ways that you can get different
color schemes that can be part of that. Thanks, good question. Audience member: Hi. I’m using slider as
an example for my question. Slider today has several skins which work really, really well,
but it’s awkward to have one theme and select a couple of slider styles for that. So any
thoughtsÖ On some widgets you just have one theme and that’s it, but some widgets like
slider and buttons are candidates for having within one theme different styles per se,
so any thoughts on making that easy so you choose the skin and the different styles of
what component are part of it rather than choosing like a style per component or skin
per component? Jeff: Yeah, the sliders are a little tougher
nut to crack because right now they’re image based. We’re thinking on that one but I don’t
have a good answer for you today. Audience member: Thank you. Audience member: Over here. Audience member: What about accessibility
sort of colors? Will that also be taken care in the options? Jeff: Can you say that again? Audience member: Accessibility colors, like
for color blind and stuff like that for people. Jeff: Accessibility color blindness issues,
yeah. That will be the responsibility of the person who is picking the key color and picking
the color scheme or building your own color scheme, which again, is about this many lines
to say how those blocks relate to each other. Then you can totally control which colors
appear, which hues appear in the color scheme. Audience member: Okay. Jeff: So that’s a good point ñ maybe we
should offer a couple of color schemes specifically designed at visual accessibility. Great, thank
you. Audience member: I have another question. Jeff: Yeah. Audience member: What about if there is a
situation where I want the colors to dynamically change on the user’s choice? I want to give
an application that the user can change the color scheme dynamically, something sort of
design UI based, something like that, which is not CSS file based something, but it’s
all in memory sort of. Will this help in that situation also? Jeff: Dynamically changing color schemes is
probably going to go over my head. Matt: He wants you to show him the tool. Jeff: What’s that? Matt: We’re demonstrating the toolÖ Jenny: Can we get a mic on him please? Jeff: Yeah, can Matt have a mic? Matt: It sounded like you were asking for
something like the tool that Jeff demonstrated. Jeff: I think he means in real time if an
end user wants to do something with an application that will change the color scheme in real
time. Matt: Unless I’m missing it I guess I would
imagine you would build something almost exactly like what you demonstrated there, which was
skinning the widgets on the fly. Jeff: Yeah. Okay. Audience member: I have one question over
here. Audience member: I had the exact same question.
The application that we work on has the capability of user choosing themes, which are nothing
but style sheets. I would probably think that it’ll be useful for us to have like a generator
where I can pick and it can build the style sheets for me using Y.Color. The last question
is related probably. Jeff: That’s exactly what you saw today.
It’s going to be a tool and you’ll be able to do that, press a button, and get a style
sheet. Also you can keep pressing a button and keep making style sheets and then you’ll
have them. Audience member: Okay, thanks. Jeff: I think we should cut off the Q&A now
and give some time to Tilo for his presentation. Thank you very much, those were good questions. [applause] Tilo Mitra: Hey everyone. I want to thank
Jeff, that was a really nice presentation. I just want to take a little bit of time to
give you a really quick look at something that I’ve been working on for a few weeks
after I joined YUI, which is responsive grids. How many people here have used YUI grids at
some point? Okay. How many of you guys are actually using it now in like a project or
something? Okay, that’s pretty good. Yeah, I’ve used YUI grids before obviously,
and I really liked it, but I felt it was always missing responsiveness. Just out of curiosity,
how many of you guys who are using YUI grids have some responsive behavior built into that
grid? Okay, a lot less hands. I wanted to just show you something that I
was working on to solve this problem. I’ve made a version of YUI grids, and this an exampleÖ
Well, let me first talk about the issue here. There are a lot of responsive grids out there
ñ Bootstraps, Zurb, and there are a whole bunch more ñ and I took a step back and thought
about the issue. Matt actually helped me a lot with this. I feel that for a lot of responsive
grids out there, they kind of force a lot of things down your throat. Let’s take Bootstrap as an example. The Bootstrap
grid is a 12 column grid layout, and if you want it to be responsive it has certain media
queries that it feels are right, so usually it’s 480 pixels and below for phones. It
has a tablet media query level, usually 768 pixels to 1024 for the iPad. Then it has a
large desktop media query. Well now you have the Nexus 7 out, we have iPad minis out, and
some of those lines are getting more and more blurry. That’s one issue. The other issue
is this 12 column layout that a lot of grids kind of force down your throat. I wanted to show you something that we’ve
been working on here at YUI, which is this grid builder. We think that a grid should
work to help you build the application that you want, and you shouldn’t be trying to
conform your application into a specific grid. What we have here is a grid builder and you
can choose a bunch of properties here and it automatically generates the CSS for you.
For example, I can take anywhere from a 12 column to a 24 column through a 36 column,
whatever I want. You can choose a specific width, or you can have a fluid or fixed grid.
Let’s see if I canÖ You can choose whether you want to make it responsive or not, and
the CSS here is actually going to be generated on the fly. I have let’s say a 6 column grid,
you’ll see a lot less CSS. The other thing that we were working on is
media query support. Instead of forcing specific media queries down your throat, right now
there are these 4, but the goal is to add more media queries and you can add or remove
the ones that you’re interested in. Let’s just keep them in here for now. I have to
fix that little bug there. The other thing we were working on is responsiveness.
Do you want your grid to be responsive or not responsive? Responsiveness, for certain
behaviors, the grid goes down to 100 per cent collapsible columns under specific screen
width. If we take this now, take the CSS that’s
generated from this, and put it into an example. Here’s a simple page with the grid overlaid
on top of it so you can see what’s going on. It behaves just like a responsive site
would, so you have images there, you have columns that go down 100 per cent width, but
you can also keep grids on mobile devices. So if you have an iPhone and you want something
to be a grid on a smaller screen size, you can do that. We’ve also baked in some support
that was lacking in original YUI CSS which is stuff like margin support, offsets, gutters,
etcetera. I just want to explain how this works. The
whole concept behind this is one class name that we’ve added to the existing YUI grids,
which is yui-g responsive. If you add a yui-g responsive instead of a yui-g, everything
inside that automatically becomes responsive. You can mix and match responsive behavior
with unresponsive behavior, just like we have on this site. Here we have a yui-g responsive
with a yui3-u-1-3, and that gives you responsiveÖ Sorry. Here we have a yui-g responsive with
yui-1-4, and that gives you the responsive behavior of an element that’s one fourth
at higher screen resolutions but at lower screen widths it comes down to 100 per cent.
Whereas in this case, we have a simple yui-g, no responsive behavior, and that maintains
sort of a grid even on smaller screen sizes. This is something that I’m still kind of
working on. One of the issues that I’d like some feedback on, actually, is what happens
to grids between the phone resolution and the desktop resolution. In that tablet space
where I think there’s an opportunity there to have grids that can offer some unique benefitsÖ
For example, let’s take a look at this one fourth column again. If you have thoughts
about this, please come and talk to me because this is still something that we’re working
on. On a tablet you could imagine this becoming two columns and two columns almost like a
little grid, and then on a phone becoming one single column again. That’s something
that we’re working on, seeing how grids can behave on a tablet versus on a phone versus
on a desktop. But the whole idea behind how we’re approaching this is that instead of
giving you one CSS file and saying this is your responsive grid as a lot of other people
do, we’re letting you generate differentÖ letting you essentially customize your grid
to how you see fit and then based on that we might take specific variations and put
them on the CDN as standard CSS files, which we feel are the most common ones that people
would need. This tool is actually out already. As you
can see, you can view it on, it’s on Heroku right now, and we might pull it in to YUI
Library when it’s a bit more fully baked. But I just wanted to give you guys a sneak
peak at it. It’s all written in JavaScript and Node and it’s actually using YUI grids
to generate YUI grids. So yeah, that’s all I had to show you today. [applause] Jenny: You first, over here. Audience member: Hello. Tilo: Oh, sorry. Audience member: So I saw you generating a
CSS we typically use to generate a mark up, and CSS typically is tied with the YUI. So
I couldn’t understand. Could you just go through the CSS which is getting generated? Tilo: Right, so the CSS that’s getting generated
actually is generated through Handlebars. The grid allows you to customize not only,
as you saw, widths and columns, but also the prefix of the CSS itself. Instead of using
yui3-g, if you could use yui3-row, and instead of using yui3-colunm you could use y-column.
Everything is generated using Handlebars, so that’s how it’s working under the hood.
The variables are getting fed into a Handlebars template that generates the CSS for you. That also actually reminds me ñ the grid
is actually, in terms of file size, is actually pretty small because what we say is if you
have responsive behavior then you have media query support, and if you have media query
support then you probably have CSS 3 support in the browser. For everything inside the
media query we use CSS 3 attribute selectors, which lets us essentially group a lot of CSS
styles into a single rule. The CSS that’s generated is actually pretty small. I think
for a 12 column grid it’s about maybe I think it was 9 kb of CSS. I have to double check,
but it was actually smaller than the Bootstrap grid CSS from what I remember. Audience member: If you can use the grid builder
on the Heroku app, couldn’t you just generate CSS inside of the app that you’re actively
creating downloads and things like that? Tilo: Totally, and in the grid builder app
you saw the yellow columns, and that’s actually the CSS getting fed into that yellow column,
that yellow sort of bar. That’s essentially what’s happening in that. Audience member: So how’s the performance
on that like if you’re doing it inside of a whole bunch of views and things like that? Tilo: I haven’t tested performance on that
specifically because I think that grids are something thatÖ It’s probably something
that most people will pull down from CDN, so I don’t have specific performance metrics
on that to show you today. Cool. Okay, cool, thanks. [applause]