Showing posts with label design. Show all posts
Showing posts with label design. Show all posts

Friday, March 28, 2008

A way to think about design for the naïve hacker

Any technology goes through its phases. First, there's the discovery of what it is, and along with it, the implementation. Just actually getting it to work is exciting. and at this stage, obviously usability is really a second thought. It's really hard enough getting it running in the first place, because of all the details you have to juggle as an innovative maker.

As a result, we get cool things that are hard to use. The first washing machines in the early 1800's didn't actually have a plug, because there were no wall sockets. Where'd you plug it in then? Your light socket hanging from the ceiling. That's right. You'd have to unscrew your lightbulb, and then screw in your washing machine--all the while on a step ladder. If it went haywire, you'd have a heck of a time unplugging it, and that probably wasn't considered very user friendly.

Then there's the phase where we've got a handle on how to build it, and the question becomes, how do we make it easy to use and work well? This is the part where design comes in. In large part, we've become specialized in what we do. The innovative builders make things that weren't yet possible possible, and then designers come along and create and experience around it. Stereotypically, hackers scoff at design. "It's just icing on the cake!" one might say. To that, I say, to scoff at design as a hacker is to scoff at implementation as a theorist--both lack appreciation of what's required.

I don't think it hurts for a hacker to know more about design, and how to think about it. While you may never be as good at putting on the gloss as a professional, you'll be better able to bridge the gap, which will help you be a better hacker. Besides, it will help you when you design APIs or public interfaces to your classes.

On the other hand, you might not need much convincing as a hacker if you are a fan of Apple's products. The Macbook Air, the iPod, the iPhone are widely touted as the stunning examples of design in technology. However, as a hacker, it seems like a strange and touchy-feely territory that relies on "taste" and "intuition". It doesn't have to be that way, as I think there's a good way to approach design, if you can think about it in the right way.

When people think "design", they usually think of superfluous, yet oddly satisfying lines that swoop or swoosh. They think of chrome, reflection, or shiny surfaces. Especially gradients. whoo. Lots of smart people in the actual design world have written entire books on "What is design?". I've never read any of them. However, I don't think they would do a good job of explaining it in a way that's easier for hackers to think about.

While there is inherent joy in design for its own sake, I'm going to only tackle a way to think about design for products. Web products, specifically.

I think of design as a deliberate attention to detail about the exposed public interface, to do two things: 1) to communicate to your user what your product is, and who your user is to others. 2) to control a user's experience

If you have no idea what your product is yet, or is still figuring it out, then there's no point in doing too much design. Design rests very much on what your product is, how it will benefit your user. If you haven't figured that out yet, ignore all the comments about how ugly your web app is, and figure out how to make it something that works, or something that people want to use because it's useful, because remember, design isn't about gradients.

The first part of what I think of as design is communication, usually with little to no words. It answers questions like, "What is it?" "How will it benefit me?" While the questions are deceptively simple, if new users can't tell immediately what it is, and how they'll benefit, they'll move on quickly. This is something I'm still working on, since people, no matter who they are, have limited time and attention to pay to any new thing. Pretend like you're talking to your really smart uncle, that is drunk all the time: Get to the simple point, and make it obvious.

Beyond the first impression, design of the product has to communicate to the user all the time, to answer questions such as, "How do I do [something I want to do]?" or "Can I do [something I wish I could do?" This is why designers talk about affordances of buttons, interfaces, and how the nipple is the only intuitive interface.

Last major point on communication, design should allow the product to communicate to the user what the heck it's doing at any one moment that the user would care about at that moment. If you're at a restaurant, you'd like your waitress to tell you that the food will be arriving in 10 mins, but you don't care what kind of pot the chef is using.

This sort of emphasis is captured in Don Norman's book on Design of Everyday Things, where he goes into detail ad nauseum. I recommend you take a look at it.

The second part, which is the result of communication, is controlling the user experience. Of course, the phrase, "user experience" is throw around a lot, and people don't much say what it is either. One aspect is how a user feels during and after using your product. Do they feel happy? Confident? Like they're having fun? Or do they feel frustrated? Powerless? Incompetent? Or wasting time?

Seems like you can't control whether someone's having a bad day or not, but there are certain tricks that designers employ to conjure up feelings. This works because our brains do a lot in interpreting colors and shapes in our culture. If you see a felt red with a holly green, you'll automatically think of Christmas, and perhaps your feelings that go with Christmas.. Change the tint slightly, and you can be reminded of traffic lights. The designer doesn't actually have to do very much, other than to suggest a mood, and the brain will do the rest. It's a good hack.

Lastly, part of the user experience is also what using the product says about them to their peers. This differs from peer group to peer groups, but they all have the same goal: everyone wants to have their product that brings them higher status in their peer group. This can be achieved by catering to core values of the peer group. If the peer group of your users are business people, they'll likely to value efficiency, reliability, and effectiveness. If the peer group of your users are hackers, they'll likely to value ability, interestingness, and functionality. If the design of your product can communicate that a user is more [insert core values] in their peer group, they'll probably appreciate the product without knowing why.

One word of caution is that it's hard to identify the core values of a group if you're not a part of it. And you can't start with an associated design and work your way back to the core values of a peer group. This is why a lot of people, when designing for girls, automatically start throwing pink everywhere, and then when the design fails, they wonder where their design went wrong. If your design doesn't communicate first what it does for a user, and second who a user is, then it's going to fail, no matter how much pink you put on there.

I recently revamped the layout and the look and feel of mobtropolis, which is why there's this post, and the two week hiatus from posting. You can see a "before" and an "after".





Before (v0.2)



After (v0.6)



While I can't claim to be a designer by any means, I feel that I was fairly successful in changing the direction of the app as well as invoking a better user experience. Communication will be an iterative process, as there will need to be some back and forth with users, before things get completely resolved. Just going through it gave me some thoughts on the matter, and next time, I'll talk about the specific lessons I've learned, and maybe some things to help a hacker or two do basic design. Til then!

Monday, January 14, 2008

Getting inline-block working across browsers

In mobtropolis, there's a gallery of pictures that I have to show. While, normally, it's not too hard showing them as inline elements, if you just have pictures, it gets a bit tougher if you have a bunch of stuff that you have to inline correctly. Sites like facebook solve the problem by using a fixed layout, so you know the width of the area you can work in, and can thus use a table.

When you have a stretchable layout, however, that doesn't work. And what you need to do is use CSS, "display: inline-block" The problem is, it doesn't have consistent support across browsers. Only Opera and Safari use "display: inline-block" and "display: inline-table" correctly. IE6 and Firefox both use "display: inline" only and don't recognize inline-table and inline-block.

So thank goodness for design blogs out there: Align List Items Horizontally with CSS . His solution is a bit of a work around, but it prevents me from writing any javascript when I didn't need to.

.ib-fix li { display:-moz-inline-box; -moz-box-orient:vertical;
display:inline-block; vertical-align:top; word-wrap:break-word; }
* html .ib-fix li { display:inline; }
* + html .ib-fix li { display:inline; }
.ib-fix li > * { display:table; table-layout:fixed; overflow:hidden; }

I should add that you need to also add this to get it to work in IE6, and get rid of the weird padding on the left:

ul.ib-fix
{
list-style: none inside none;
padding: 0px;
}

Yay. small tip! As a bonus, I also found out about ie7.js, a javascript library that fixes incompatibilities in ie7 and ie6.

Wednesday, January 02, 2008

DRYing up your views with a TableBuilder

In the last two months, when I was adding more features to mobtropolis, I found it painful to try and lay things out all over again from scratch. As a result, it sucked to see ugly layouts on the new pages juxtaposed with all the styling I had done before. It wasn't until a week ago that I said to myself, "Stop the madness!" and started refactoring my views--something I never thought of doing much of until now. When you don't, the barrage of angle brackets blows out of proportion and it starts to look pretty damn fugly with complex views.

What I should be able to do is take common mini-layouts in my views and make them available as helpers so that I can think in terms of bigger chunks to piece together pages, rather than in divs and spans. In addition, it makes your interface more consistent for your users.

Some good resources were presentations from the 2007 RailsConf, like V is for Vexing and Keeping your views DRY. While a lot of view DRYing talks about form builders, I didn't see any on table builders, so I decided to take a stab at it. Personally, I don't like to overuse tables for layouts. But as certain elements in my page layouts have been repeated, I refactored them into first helpers, and then when I did more than one, I extracted it out into a simple table builder. This is how you'd use it:

For example, I have a mini-layout where I show simple stats:


Here's how I used a simple table builder to display the above:

<% score_card do |sc| -%>
<% sc.placard("Votes", "", num_voters, "") -%>
<% sc.placard("Sceneshots", "", num_sceneshots, "")-%>
<% sc.placard("Participants", "", num_participants, "") -%>
<% sc.placard("Challenges","", num_challenges, "") -%>
<% end -%>

And I find that I started using the same sort of thing in other places, like in a user's profile:



<% score_card do |sc| -%>
<% sc.placard("Submitted Scenes", "", num_scenes, "") -%>
<% sc.placard("Submitted Sceneshots", "", num_sceneshots, "") -%>
<% end -%>

I cut out some details so you can see that it's just a block that gets passed a ScoreCard object, from which you call placard to add another score to the score_card. It sure beats writing <table> and <td> over and over again.

To declare the helper, we create a helper with the structure of the table inside the declaration of a ScoreCard object. We have a ScoreCard object to hold the contents of the placards. When they're called in the block above in the template, they get stored in the ScoreCard object, and not written out to erb immediately. That way, we can place them wherever in the table we please, by making the call to card.display(:placards):

module ScorecardHelper
def score_card(html_options = {}, &block)
options = { :class => :scorecard, :width => "100%" }.merge(html_options)
ScoreCard.new(block) do |xm, card|
xm.table(options) do
xm.tr(:valign => :top) do
xm << card.display(:placards)
end
end
end
end
end

So then what's ScoreCard look like? Pretty simple. It has a call to each cell that can be filled in the mini-layout. It's kinda analogous to how form_for passes in a form object, on which you can call text_field, etc.

require 'lib/table_builder'

# Used to create a scorecard helper
class ScoreCard < TableBuilder
cells :placards

# a placard is placed into scorecards
def placard(text, text_id, score, widget)
xm = Builder::XmlMarkup.new
@placards[:html] += xm.td do
xm.span(:style => "font-size: 1.2em") { xm << "#{text}" }
xm.em(:id => text_id, :class => "primary_number") { xm << "#{score}" }
xm << "#{widget}"
end
end

end

Notice that there's a call to cells() to initialize the type of cell, and a method of the same name that builds the html for that cell. If you have other types of cells, you simply put it in the list of cells, and then create a method for it that is called in the template. By convention, you'd stick the html of the cell contents in "@#{name_of_cell}"[:html], and if you wanted, pass in the html_options, and stick it in "@#{name_of_cell}"[:options]. Then, you can access those in the helper wherever you want.

Let's try another one. I have a mini_layout with a picture, and some caption underneath it, like a polaroid.


<% polaroid_card do |p_card| -%>
<% p_card.picture do -%>
<%= sceneshot_for scene -%>
<% end -%>
<% p_card.caption do -%>
<%= pluralize(scene.num_of_sceneshots, "sceneshot") %>
<% end -%>
<% end -%>

The associated helper and PolaroidCard object are pretty simple:

module PolaroidCardHelper
# a polaroid card is used to show a picture with a caption below it.
def polaroid_card(html_options = {}, &block)
options = { :class => :polaroidcard, :style => "display: inline;" }
PolaroidCard.new(block) do |xm, card|
xm.table(options) do
xm.tr { xm.td(card.html_options(:picture)) {
xm << card.display(:picture)
}}
xm.tr { xm.td(card.html_options(:caption).merge(:class => "caption")) {
xm << card.display(:caption)
}}
end
end
end
end


require 'lib/table_builder'

class PolaroidCard < TableBuilder
cells :picture, :caption

def picture(html_options = {}, &block)
@picture[:html] = capture(&block)
@picture[:options] = html_options
end

def caption(html_options = {}, &block)
@caption[:html] = capture(&block)
@caption[:options] = html_options
end
end


I've tried to pull all the plumbing out into TableBuilder (dropped it into lib/), and only leave the flexibility of creating the table structure in the helper, and the format of the cell in the object. It ends up TableBuilder isn't too complex either. It uses some metaprogramming to create some instance variables. I know it pollutes the object instance variable namespace, but I wanted to be able to say @caption[:html], rather than @cells[:caption][:html].


class TableBuilder < ActionView::Base
class << self
# used in the child class declaration to name and initialize cells
def cells(*names)
define_method(:initialize_cells) do
@cell_names = names.map { |n| "@#{n}".to_sym }
@cell_names.each do |name|
if instance_variable_defined?(name)
raise Exception.new("name clash with ActionView::Base instance variables")
end
instance_variable_set(name, { :html => "", :options => {} })
end
end
end
end

def initialize(decor_block, &table_block)
super
initialize_cells
decor_block.call(self)
html = table_block.call(Builder::XmlMarkup.new, self)
concat(html, decor_block.binding)
end

def display(cell_name)
instance_variable_get("@#{cell_name}")[:html]
end

def html_options(cell_name)
instance_variable_get("@#{cell_name}")[:options]
end

end


I've found have these helpers cleans up my views significantly, though I have to admit, it's not exactly easiest to use yet. In addition, I'm not exactly thrilled about having TableBuilder inherit from ActionView::Base, but it was the only way I could figure out to get the call to concat() to work. In any case, the point is to show you that refactoring your views into helpers is a good idea, and even something like a table builder goes a long way, even if you don't do it the way I did it. Lemme know if this helps or hinders you. snippet!

Thursday, May 03, 2007

Innovation is force fed; someone get the lube!

In an earlier post, I had talked about what users know and what you know, when it comes to listening to your users. That said, when it comes to building new products, either in another line, or something to replace your old product, you should go back to not listening to your users--at least on the first draft. The act of creation is effectively the effort of one (or the few). At least when it comes to first drafts, too many cooks do spoil the broth. That might be a bit Ayn Randian, but the only thing I've ever heard of where design by committee was successful was the Space Shuttle and the Lunar Lander. (If there's more examples, please enlighten me.)

When you're building a product you're essentially forcing your world view onto others. You're basically saying, "I find this to be a pain. And this is not the world as it should be. As a builder, I can correct it after mouthing off for a while." And this is usually why people don't warm up to innovative ideas readily--someone is shoving their world view in your face. And unless you're someone that has been looking for a solution to the same problem when it's introduced to you, you won't be receptive to it. Even innovative people suffer from this affliction of shortsightedness.

“Don’t worry about people stealing an idea. If it’s original, you will have to ram it down their throats.” – Howard Aiken

Because innovative products can be so jarring, they should soften the blow a bit--or as others like to call it lowering the barriers. This is where influences from design, gaming, and etiquette can help.

Beyond the current trend of sleek lines, horn-rimmed glasses and black turtle necks of designers, design isn't just about putting a gradient background on your web app, or painting things in pastel colors. Hackers making a product should understand that design is the study of how to best solve communication and usability problems with limiting constraints. What information would the user need to know right this second, and how should you convey it to make it as easy to understand as possible? And from the answers to those questions will emerge a form that is also pleasing to the eye.

Gaming is an avenue more familiar to hackers than design is. However, games are often seen as mere trifles of play reserved for kids--though this is changing. If you've played enough video games and thought about WHY they're fun, will help also, because to bring out the essence of fun in what's normally perceived as tedium will give your product an edge. In the lecture about the ESP game by Luis von Ahn, he laments the fact that there's millions of cycles of human computation wasted. There was 9 billion hours played of solitaire last year (est.). Considering that the Empire State Building took 7 million hours and Panama Canal took 10 million hours, that's a lot of wasted hours. We should be able to put those cycles to good use by making people play games to solve problems that computers can't yet solve. So a symbiosis of humans and computers can be considered a large distributed computer to solve hard problems, such as object recognition in images. You might have played it.

In other web apps, the idea of a collection is a powerful mechanism of play. Social networking sites play on the idea of collecting friends, much in the same way that in Pokemon, you "gotta catch them all!". In others, the idea of a scoreboard is a powerful motivator, as seen on Digg and Reddit.

And last of all, the idea of etiquette seems far removed from being applicable to innovative products. However, no matter how much technology people surround themselves with, we are still social beings and will have social tendencies. Because of that, we expect certain behaviors and interactions between ourselves and our machines. We get mad and frustrated at computers and devices because they're usually not very polite. They stop responding when they're busy doing something, but don't tell you what they're doing. They don't remember what you told them last time and asks us over and over again. And when they don't know how to ask for help when something goes wrong, since the error messages are unintelligible to most users. These are all hallmarks of an annoying person, and were it a real person, I'd have kick them to the curb.

The iPod, and in general, Apple products, are known for their politeness. When I first got an iPod, it was the 5th generation. I was surprised that it stopped the music, if the ear buds got unplugged, and that it turned itself off, after it's been paused for a while. Basically, it knew what was going on, and reacted to it in a fashion that makes sense to its owner. That sounds like the promise of Agent based software hyped so long ago. Maybe it should make a slow come-back.

The sad thing is, computer apps and devices have been annoying us for so long, that we have kinda gotten use to it. I think as research on classifiers become more readily available to programmers as being embedded in the language, and the rising influence of designers in applications, we should see a trend towards more polite products. If you can make a product that is polite, it'll go a long way in gathering fans.

In the end, you want people to use what you build if it has value. And users want to GET THINGS DONE, so they can move on with their lives. All products should solve problems, there's no doubt that it's essential. All other points are moot if your product is useless. But given that it does solve a problem, if it is also beautiful, fun, and polite, it will go a long way in lowering barriers so that we can all have pearls Before Breakfast.