Moved here
Previously.
Web Typography
Jason Santa Maria, self-described font geek, gave a talk on fonts in general and the huge opportunities sites like TypeKit give for improving the look and feel of your own sites. As recently as three years ago, we were still stuck using the 18 Web Fonts or hacks like sIFR to get real fonts on websites. Publications like the New Yorker, who are instantly recognizable from the typefaces they use, have had a hard time carrying that brand identity to their websites. Many have resorted to rendering each headline in the correct font into images, creating a maintenance nightmare. With the ability to use things like font-face , existing publications can carry their brand identity with them to the web, and new publications can enhance their own identity with their font selections. Even simple things like the ability to use condensed fonts on the web has totally changed the way well-designed sites look.
How do I choose the right typeface?
There is a difference between display typefaces, which are most useful for headlines and titles, and text typefaces, which are best for body text. A display typeface like Bodoni can be great for an article's title, but can be difficult to read in a smaller size. Most people will have the best bet finding a few good fonts with many faces, like FF Meta or Proxima that can be used successfully in many different scenarios. Good typography is about building contrast, texture, and feeling on the page that help with comprehension and enjoyment of the thing being read. "Good typography is invisible."
When using typography, there are a few guidelines that can be useful, but should be broken when appropriate:
- All things being equal, err on the side of the text size being too big
- Contrast is the most important tenet of good design - make parts of the text that differ in some way look different. Play with size, weight, coler, etc. to build the right amount of contrast.
- Line spacing should be directly proportional to the line length and color of the font
There are lots of things to consider when picking the right fonts, but there are ways to help choose among them. Noting the qualities you want your site and your content to convey will help you narrow long lists of fonts down. Finding fonts that come in both serif and sans serif faces can make pairing simpler and will help create contrast where it's needed. Fonts that have the same feel as a commonly used font can help you pick similar but more interesting fonts. And finally, trying different fonts out by setting some text in a range of typefaces and playing with them will almost always be really helpful. Jason left us with a few sites that use web typography well: Lost World's Fairs and Voltage: Fashion Amplified.
Why Designers Fail
Scott Berkun was next, with a talk about design failures and how to learn from them. Mindful practice and studying other people are the two primary ways people learn new things, and studying failure in particular is an incredible untapped resource of knowledge. In the design world, it's difficult to find good analysis of design failures. Especially in fields like architecture, where highly in-demand designers tend to move on to new projects frequently, it's sometimes difficult for these designers to see and hear the feedback that only comes from using the product. How do you avoid falling into common design traps? Understand that all designers fail most of the time (whether it's on the drawing board or in the real world), own the mistakes that you do make, analyze the cause (whether it's setting the wrong goals or failing to meet them), and studying common causes of failure and possible mitigations.
Scott showed an amazing video of designer Matt Wiley laying out a magazine article, and how many intermediate failure states he went through in the process of coming up with a great design. These are the things that many people don't see when they look at a finished product. There's a constant process of experimenting, failing, and revising that is absolutely critical to the creative process, but isn't visible at all in the final product.
So why do designers fail? Some of it comes from falling into common traps: problem-solving for its own sake (Puzzle traps), obsessively categorizing (Category traps), confusing measurements with reality (Number traps), and loving the sketch more that what it actually represents (Drawing traps). Some of it is related to skill: there's often a mismatch between the skills that designers have and the skills that an organization needs, especially around persuading decision-makers that good ideas are good ideas. Designers should be "ambassadors for good ideas", and that involves knowing how to sell these ideas to decision makers. Luckily, persuasion, like most skills, can be learned, and it is something that designers should take the time to learn.
Scott did some research to discover which of these failures were the most common and the most important. By far, the biggest overall issue was organizational: "Non-designers making design decisions." Others were psychological: Not seeking enough data before designing, and not being receptive to feedback. Less common and important were skill-related issues, with the top being lack of awareness of business fundamentals, and lack of idea-pitching skills. Scott publicly posted his data, which is really worth a look, and his slides are also available. Hopefully, being able to identify these causes of failure will help us, as a profession, avoid them in our future work.
Moved here
I went to An Event Apart Seattle last week and it was the best conference I've ever been to. I've started to gather my notes together and will, over the next few posts, talk about what I was able to take away from each of the presentations. Before I go in detail on the talks, though, I wanted to mention a few themes that seemed to flow through multiple presentations during the conference.
HTML5 and CSS3 are ready now.
This theme was the most related to the work I do on a daily basis. Every talk that touched web development explored features that weren't available in every browser (one talk was built around them), and it seems that the web design community has finally abandoned the idea that sites should act the same way in every browser. Most of the speakers accepted that sites should still work in some way in every browser, but it seemed accepted as given that we should start using HTML5 and CSS3 features tastefully to enhance the experience of people using modern browsers (and especially modern mobile browsers).
Context is everything
Whether it was talking about simplicity as delivering the correct experience to the correct user or pairing the correct font or design or architecture or business goal to the correct content or tailoring a mobile web application to fit the behavior of a user who just wants to get the information they need and get on with their life, context was brought up constantly. Building the right context for the job you're trying to do involves asking pointed, well-thought-out questions, and several of the presentations had slide after slide of thought-provoking questions intended to bring the assumed context out into the open.
The things we have been talking about as important before, are now critical
From the data some of the presentations revealed, we are beginning to hit a critical moment in many areas the web touches. Mobile is the most obvious example: Luke Wroblewski was fond of mentioning that smartphone shipments overtook PC shipments a full two years earlier than expected. There were others, too: the rise of HTML5 and CSS3, the ability of sites like TypeKit to drastically help increase your site's branding, and the growth of content strategy as a discipline to deliver the right information to the right people at the right time. These things that were important before are now becoming necessary, because people are beginning to expect great experiences from the sites they visit, and if they can't get it from your site, they will get it somewhere else.
A whirlwind tour of the history of the web
Jeffrey Zeldman started the conference with a fast and funny history of the web. Beginning with Movable Type (Gutenberg, not Six Apart), passing through AOL, the browser wars, an unfortunately de-pixelated Zeldman on the cover of "Designing with Web Standards", and the fall of XHTML, and finishing up with mobile browsing, HTML5/CSS3, web fonts, and the IE9 beta, it did a fantastic job of setting the context for the rest of the conference. Today, thanks to the widespread adoption of web standards, the web has grown up. We can now make use of skills that were not previously useful online to create a better web experience for everyone. And since the web is now everywhere, those experiences can affect people beyond what was previously possible.
Better user experiences through psychology
Sarah Parmenter was next, giving a talk called "Crafting User Experiences." Sarah discussed how well-known psychological principles could be used to build great user experiences. I noticed a lot of parallels to one of my favorite recent books, Predictably Irrational (which should be next on your list if you haven't yet had the opportunity to read it), and I found it really interesting that a lot of this knowledge that used to seem to be confined to sales and marketing is now making its way into mainstream web design.
Snap judgements and sensation transference were the first topics hit. We usually deny that snap judgements are as important as they are, since we tend to thing that "the quality of a decision is directly related to the time and effort that went into making it." In reality, judgements made in under 5 minutes are drastically more important to our final decisions than we realize. This is related to to sensation transference: our judgement of the packaging of a product can have real effects on our perception of the actual product. For example, adding yellow coloring to 7up made people percieve more lime taste while drinking it. This led to the first appearance of the Dark Patterns wiki during the conference. Like any skill, though, these skills can be used for good as well as evil.
Better user experiences through psychology can be delivered by concentrating on speed, simplicity, surprise, social proof, and stirring emotions. Speed involves getting people the product they want quickly without any fuss. From working closely on your site's structure and building great navigation to creating quick video overviews of a service, the faster you can get information to someone, the happier they'll be. This goes hand-in-hand with simplicity. Simplicity is delivering the right content to the right person at the right time, and involves really studying your audience and what context they're in when they visit your site. Simplicity is heavily colored by perception, and this can be used to your advantage—telling your users it will take 30 seconds to sign up on your site when it actually takes 10 will make your site seem easier to use and will make that user feel smarter and better about themselves.
Surprise is challenging the expectations of your users in some small way. You can evoke this by using complimentary highlight colors on calls to action, or provoking the user to ask questions they need to click through to answer (what is that weird old tip for a tiny belly?), or even picking a product or company name that provokes surprise and questions from a user (like Sarah herself did with "You Know Who"). Social proof is as simple as pre-filling a tip jar (so it seems normal to leave money) or public follow/like counts on your site (30 million people and counting apparently love FarmVille). Finally, Sarah talked about how people are much more likely to make judgments on emotion and only later justify them rationally. Things like using lots of whitespace for luxury items or natural textures for organic items create an emotional attraction that can work beautifully when trying to attract a user to your products. Etsy does a great job of this, as did Apple when creating a "How it's made" for their iPhoto holiday cards. Anthropomorphized icons can also work great for creating an emotional attachment, and can be as easy as adding eyes and a smile to, say, an illustration of a house. There was a ton of great stuff in her talk, and I'm looking forward to trying some of the things she mentioned myself.
Moved here
As
I mentioned
last week, we've started to
use HipChat for our internal
chat here at Avvo. I've decided to
forego their default AIR app and
try running
their web interface through Fluid to get a more native mac-like
experience. One of the few things that bothered me about the Fluid
app was the lack of keyboard controls for accessing and closing the
tabs for each chat
room. So I
added them.
One of the great things about Fluid is its support for userscripts
to add functionality to the pages it wraps. I created a userscript
for the HipChat web interface that adds the following keyboard
shortcuts:
- Command-W: Closes the current tab (if the tab can be closed)
- Command-1 through Command-9: Selects the tab at the specified position
- Command-{ and Command-}: Select the previous tab and next tab, respectively.
To install it, when running your HipChat fluid instance, open
'Preferences', go to the 'Advanced' section and add *.user.js as a
pattern. Then, go to 'File', 'Open Location' and paste in the following url:
https://github.com/justinweiss/hipchat_shortcut_keys/raw/master/hipchat_shortcut_keys.user.js
Install the script when it asks, and you should be good to go.
Moved here
We've been
investigating HipChat for
internal chat lately, and I was a little disappointed that it used
Adobe AIR on all platforms, instead of providing a native app. To be
fair, it's the most well-done AIR app this side of Basalmiq, but
there were still the little inconsistencies that I couldn't help but
get annoyed by. I'm not going to describe the exact things that
bothered me here,
since Alex
Payne does such a great job describing the problems here, but it
felt weird enough that I couldn't see myself using the app as often
as I'd use, say, Propane.
Heading through their support pages to get an idea of how I could
improve my experience, I noticed that there seemed to be a fair
amount of support for running the web version of HipChat
through Fluid, which, while not
as pleasant as a native app, still felt much faster and more native
on the Mac than the AIR app did. HipChat did an amazing job of
building support for things like Growl notifications and Dock badge
notifications into the web interface, and seem to want to make the
site work as well in Fluid as it does through AIR. Given the
constraints they've set for themselves, HipChat has done a
tremendous job of building support for people that would like an
alternative to AIR.
Accessing HipChat through Fluid
There were only a few minor gotchas I ran into when creating the
HipChat Fluid app. First, after downloading and running Fluid, the
URL should be set to https://www.hipchat.com/chat . The
icon can be left as the website favicon, which awesomely still takes
a full-size icon from HipChat's site, rather than the actual tiny
favicon the site uses. Once inside the HipChat Fluid app, you'll
have to adjust the preferences. Under the Advanced
category, you'll have to add the
pattern *hipchat.com/sign_in* , otherwise you won't be
able to sign in. Finally, if you want Growl and Dock notifications,
you have to enable notifications through the AIR app—HipChat
doesn't expose that setting in the web app.
There are some cons to running the web app—although
notifications, file drag-and-drop, and most of the other
functionality works, the web app is missing the file/link dock on
the right hand side, the tabs are a little flakier, you can't video
chat, and there's a big "Download App" button that would be much
more valuable as room for another chat tab. However, the use of
native Mac OS X controls, the smoother scrolling, Growl support
(rather than the built-in notifications that show up in the wrong
place), better integration with the system, and the ability to script
the site with CSS and JavaScript make the Fluid app the way to go.
Finally, while I was digging through the JavaScript, I noticed that
HipChat has what looks like decent support for running it as a
Chrome app, which might be worth trying if you're not on OS X or
just prefer Chrome.
Moved here
Deploying software builds that don't pass the unit tests is a waste
of time. It prevents QA from doing their job, it causes delays while
a fixed build is prepared, and it could even lead to more ops time
to fix if the broken build breaks something on the machines it's
being deployed to. If you use
Jenkins to run your tests,
there's a better way. Using the Jenkins API, we can get the last
revision that passed for a project, and adjust our behavior based on
that revision number.
Jenkins.rb is a
Ruby wrapper for the Jenkins API. gem install
jenkins.rb should give you everything you need. Then, you can
whip up a quick ruby script:
require 'jenkins'
require 'fileutils'
Jenkins::Api.setup_base_url(:host => 'jenkins-host.example.com')
# Pull the project name from the command line
project = ARGV[0]
# Retrieve the last successful build number (123, for example)
successful_build = Jenkins::Api.job(project)["lastSuccessfulBuild"]["number"]
# Retrieve information about the actual build, and grab the last built revision out of it
build_info = Jenkins::Api.get("/job/#{project}/#{successful_build}/api/json")
sha = build_info['actions'].detect {|h| h["lastBuiltRevision"] }["lastBuiltRevision"]["SHA1"]
puts sha
When this script is run with ruby script.rb
project-name , it'll return the most recent revision of
project-name that passed all the tests, which the build script can
use by calling git reset --hard number or
something else like that.
Moved here
will_paginate
makes pagination easy, and we use it whenever we need to show lots
of ActiveRecord objects. Once the will_paginate gem is installed, it
provides a built-in #paginate method for classes that
inherit from ActiveRecord::Base , but we still had our
own crazy hacked-up pagination for the responses we would get back
from solr. In the interest of making things more consistent and
deleting as much code as possible, we thought it'd be a good idea to
look into getting our solr objects into the will_paginate model.
This turned out to be much easier than
expected. The will_paginate view helper expects an
instance of WillPaginate::Collection , which is simple
enough to initialize. It was as easy as adding the following method
to our search models:
def paginated_docs(options = {})
WillPaginate::Collection.create(options[:page] || 1, options[:per_page] || PER_PAGE) do |pager|
@response = $solr_client.query(self.search_options.merge({
:offset => pager.offset,
:limit => pager.per_page}))
pager.replace(@response.docs)
# pager.replace sets total_entries if we're on the
# last page. Otherwise, we'll just populate it.
unless pager.total_entries
pager.total_entries = @response.total
end
end
As long as a WillPaginate::Collection is returned,
will_paginate will do the right thing. Something similar should work
for using other services that support limit/offset, but don't have
built-in pagination.
Moved here
After setting
up a CDN like CloudFront to quickly serve your Rails site's
assets worldwide, you'll probably want Rails to actually link to the
assets through the CDN, instead of locally. Rails makes this easy
with ActionController::Base.asset_host . The easiest way
to use asset_host is by putting the following code in a
Rails initializer or production.rb:
ActionController::Base.asset_host = "assets.example.com"
After that, calls like image_tag("logo.png") will produce
links like:
<img alt="Avvo" src="http://assets.example.com/images/logo.png?1297189653" />
Going past the basics
While this will work for very simple cases, more complicated
scenarios need more code. If you put the above snippet in an
initializer, you'll have to wrap it in
if Rails.env.production?
ActionController::Base.asset_host = "assets.example.com"
end
Why would you want to do that when you could stick the code in
production.rb? For our site, it's because we rely on configuration
set up in other initializers, which won't be ready by the time
production.rb runs.
YSlow will tell you
about the next thing that needs to be done. It turns out that some browsers
will only open two requests to any given hostname during a web
request. In order to improve the speed at which a browser can fetch
assets, you'll need to do something a little more complex with
asset_host .
Giving a Proc to asset_host
Along with taking a
string, ActionController::Base.asset_host can also take
a proc. This gives us more flexibility when generating these
hosts. We can solve the above problem by using a proc instead of a
string:
if Rails.env.production?
# Source is the asset path as a string, request is a request object
ActionController::Base.asset_host = Proc.new do |source, request|
# hosts can also be pulled from a config file for more flexibility
# or skipping the CDN in staging environments
hosts = ['http://assets1.example.com', 'http://assets2.example.com']
# Get a semirandom numeric hash that is consistent for the same source,
# to make sure that the same asset file always maps to the same asset host
# (This helps the browser cache these assets)
hash = source.hash
# Return a semirandomly selected host from the hosts array
hosts[hash % hosts.length]
end
end
Dealing with secure pages
The next thing you'll notice, especially if you don't have SSL
certificates for your asset hosts, is that you'll start getting
warnings when accessing resources on secure pages. This can be
easily fixed by adding another condition to the above proc:
if Rails.env.production?
# Source is the asset path as a string, request is a request object
ActionController::Base.asset_host = Proc.new do |source, request|
if request.ssl?
"#{request.protocol}#{request.host_with_port}"
else
# hosts can also be pulled from a config file for more flexibility
# or skipping the CDN in staging environments
hosts = ['http://assets1.example.com', 'http://assets2.example.com']
# Get a semirandom numeric hash that is consistent for the same source,
# to make sure that the same asset file always maps to the same asset host
# (This helps the browser cache these assets)
hash = source.hash
# Return a semirandomly selected host from the hosts array
hosts[hash % hosts.length]
end
end
end
It also turns out that when referring to assets in ActionMailer, you
sometimes won't get a request. We need to handle that case, too:
if Rails.env.production?
# Source is the asset path as a string, request is a request object
ActionController::Base.asset_host = Proc.new do |source, request|
if !request #request == false for emails, apparently
"http://#{AppSettings.host}"
elsif request.ssl?
"#{request.protocol}#{request.host_with_port}"
else
# hosts can also be pulled from a config file for more flexibility
# or skipping the CDN in staging environments
hosts = ['http://assets1.example.com', 'http://assets2.example.com']
# Get a semirandom numeric hash that is consistent for the same source,
# to make sure that the same asset file always maps to the same asset host
# (This helps the browser cache these assets)
hash = source.hash
# Return a semirandomly selected host from the hosts array
hosts[hash % hosts.length]
end
end
end
Dynamic assets
There also might be 'dynamic assets' that the app might use, like
captchas, images that are generated on the fly by the server, or
anything else that's not stored on the filesystem. For these you
might just want to use your web server instead of the asset server:
if Rails.env.production?
# Source is the asset path as a string, request is a request object
ActionController::Base.asset_host = Proc.new do |source, request|
# Redirect dynamic assets to the web server
is_asset = source.match(/images|javascripts|assets|stylesheets/)
if !request #request == false for emails, apparently
"http://#{AppSettings.host}"
elsif request.ssl? || !is_asset
"#{request.protocol}#{request.host_with_port}"
else
# hosts can also be pulled from a config file for more flexibility
# or skipping the CDN in staging environments
hosts = ['http://assets1.example.com', 'http://assets2.example.com']
# Get a semirandom numeric hash that is consistent for the same source,
# to make sure that the same asset file always maps to the same asset host
# (This helps the browser cache these assets)
hash = source.hash
# Return a semirandomly selected host from the hosts array
hosts[hash % hosts.length]
end
end
end
Hardcoding exceptions
Finally, you might run into some javascript libraries that don't
like being loaded from a different domain than the page being
loaded. You can hardcode these to be loaded from the web server
instead, for the final bit of code:
if Rails.env.production?
# Source is the asset path as a string, request is a request object
ActionController::Base.asset_host = Proc.new do |source, request|
# Some assets aren't happy being loaded from the assets server :-(
borked_assets = ['tiny_mce']
# Redirect dynamic assets to the web server
is_asset = source.match(/images|javascripts|assets|stylesheets/)
if !request #request == false for emails, apparently
"http://#{AppSettings.host}"
elsif request.ssl? || !is_asset || borked_assets.any? {|asset| source.match(asset)}
"#{request.protocol}#{request.host_with_port}"
else
# hosts can also be pulled from a config file for more flexibility
# or skipping the CDN in staging environments
hosts = ['http://assets1.example.com', 'http://assets2.example.com']
# Get a semirandom numeric hash that is consistent for the same source,
# to make sure that the same asset file always maps to the same asset host
# (This helps the browser cache these assets)
hash = source.hash
# Return a semirandomly selected host from the hosts array
hosts[hash % hosts.length]
end
end
end
This snippet of code should handle pretty much any asset you throw
at it, and is easily expandable using whatever type of
environment-specific configuration you use. Give it a try, and let
me know what you think.
|
|