Redesigning Yoga in the City

I’ve jokingly been complaining about being pixel pushed by design-minded people around me. But of course, even I can’t deny the power of aesthetics. There seems to lack a sense of professionalism if you can’t even get the basics of design. So, I embarked on the journey to redesign my pet project, Yoga in the City, after taking @thoughtbot’s course, Design for Developers.

Yoga in the City Design Brief

The first step was to articulate our goals for the redesign. I equate this exercise to setting your intention in yoga class. Like the lessons taught in Bhagadvad Gita, one of the premiere philosophical books on yoga, your intention determines the outcome. Borrowed from a yoga teacher: if you saw a tree that you wanted to go to. Would you aim for it or would you wander and hope to find it?

A brief summary of the business / site goals. An aggregate schedule of yoga classes, curated by a yoga snob. The app solves the problem of finding fantastic teachers at different studios. It should also encourage me to discover more yoga teachers to add to the list.

The key users. Yogis that are trying to fit their 1.5 hours of refuge into their busy schedules. They are generally young professionals, both male and female, but predominantly female and peeople that are health conscious.

Scenarios in which your app or site will be used. They should be able to get to the site quickly, process the class information easily and add class details to their calendars.

Design style guidelines to follow. Calming, connected to the world.

Design Principles

My friend’s wife is an art scientist. She studies why certain pieces of art are more appealing to others, why some become famous. It turns out there are some basic guidelines anyone can follow. Let’s critique my old design on these.

Alignment The only thing that is aligned on the left side of the design is the word “Yoga” from “Yoga in the City” and the beige schedule block. Everything else is entirely random. Although I had put a lot of thought in the original design to make things easier to read, I mistakenly thought that just meant bigger font size.

Emphasis This one I understood, although it wasn’t executed that well. I chose to emphasize the day of week “Wednesday.” Why? I’m not so sure.

Hierarchy There is an order of importance, that the eye should discern quickly. Given the font-size and colors used, “Wednesday”,the times and the title seem to compete with each other.

Consistency Patterns can be found in art, dance and music. This provides continuity, which people like. Thankully, I had used a template partial for the individual classes, and programmatically created the divs for them. What I wasn’t consistent with is colors and font sizes. They were randomly picked by me.

White Space Not by design, everything is jammed to the left, leaving lots of white space on the right. White space usually gives a feeling of airy quality, but everything still feels squished in my case. I could have used some white space between the elements.

Balance Another principal that we see in dance and music, as well as visual art. My original website is weighted heavily to the left.

Unity Lastly, everything needs to be tied together somehow. This is, perhaps, more of a feeling than a hard rule.

Grid System

According to @kylefieldler, if the question comes up, when should a grid system be used, the answer is always. This immediately reconciles any problems with alignment. There are also a lot of great frameworks to leverage off of. At work, we’re big believers of Zurb’s Foundation. For my project, I wanted something lightweight, and went with Neat. Twelve column layouts are the most popular because they can easily be converted to three or four column sub layouts. With this in mind, I sketched out a few ideas.

   

One of my concerns was being able to view the site on a phone and quickly digest the information, which is why I threw out the traditional calendar grid out the window. I also wanted to play around with a big hero image that would also fit into the grid layout. Although uncommon, I went with a two column layout, that would collapse into one on mobile.

Typography

Oliver Reichenstein wrote Web Design is 95% Typography and I think most design nerds would agree. Who would have known so much personality can be expressed through a typeface? In this part of the course we looked at the effects of line-height, line length, letter-spacing, and even the components of the font. Usually there is more than one typeface on a page, a serif and a sans serif. It’s important to find something that complements and contrasts with each other. You can try the typography dating game.

Color

Color can have different meanings in different culture. Case in point: red is considered bad luck on a trading floor. However, for Chinese people, red represents luck. So knowing the viewers and how they’ll interpret color is important. In America, blue is considered stable and professional. Red is considered dynamic, but sometimes dangerous. In my current redesign, I’m letting the backgound image do most of the color deciding for me.

Next Steps

The readability of the class details still needs work. I’ll most likely choose a different typeface and also darken the transparent, black overlay. A friend pointed out to me that she didn’t really understand what the site was about. I probably need more information there. Still a long ways to go. Hopefully, my next blog post will be about how I built the scraper that’s pulling updated schedules and how to deploy a Rails app on Heroku that uses PhantomJS. Stay tuned!

Coding HTML Emails

Emails are still the most direct way to market your product to clients. With an ROI of around 4,300% (according to the Direct Marketing Association), it’s hard to argue against it. So it was only a matter of time that I was tasked to code some emails. I naively thought it would be a simple task. Write some basic HTML/CSS. How hard could that be? But I can’t remember the last time I coded basic HTML/CSS, if ever. I started with ERB templating, and now am mostly in HAML, thanks to @tjhanley. Even the normal CSS I write is (mostly) organized through SASS. So…HTML emails, it makes me shudder just thinking about it.

Here are 5 tips for that next HTML email you need to code.

  1. If you want any styling, it better be in-line CSS. Yes, you heard me right. There’s no way to link to external style sheets. Plus, most email clients ignore style tags. It makes sense because your favorite email client doesn’t want your css possibly interfering with theirs. Thankfully, mailchimp has a nice widget that can help inline your CSS for you.
  2. Tables are your friend. Someone told me tables are very web 1.0 when it comes to design. Well, that’s the world of email. Padding does not consistently work, but cell padding on a table does, specifically td tags. Don’t forget you can nest tables within tables to create columns and such.
  3. Media queries? You can forget about them. 48% of emails are read on mobile devices (source). Any half intelligent company would want their emails to look good on a smart phone. Our favorite Gmail client on iPhone and Android do not support media queries. Bleh. Gmail client on iPhone will resize the email to fit in the viewport, so your layout isn’t distorted, but everything is hard to read. Gmail client on Android will overwrite any table widths to be 100% of the screen, a pseudo-media query. Despite Gmail’s lack of capabilities, build a responsive layout. Check out this list of 32 Responsive Email Templates by mashable.
  4. CSS functionality is severely limited. Display: none will be ignored. Background images don’t work (consistently). Absolute/relative positioning will also be ignored. Use of FONT tags are encouraged.
  5. Code for a maximum width of 600px. Think small. Think simple.

Happy coding! Meow, meow, meow.

Launched

Two weeks ago, it was finally time to unveil what I’ve been “secretly” working on. Back in July, a friend of mine recruited me to join his start up. That friend was Tristan Walker, hustler extraordinaire. That start up was Walker & Company Brands, Inc. Our mission: to make health and beauty simple for people of color.

Lots of work goes into developing and producing physical products, but we also wanted the shopping experience to be smooth (pun intended). That’s where I came in. Sweat and tears went into this itty-bitty website, getbevel.com. More to come in January. Yes, our engineering team of two is looking for engineers (don’t be shy)!

When I was graduating from Flatiron School, I contemplated the type of place I wanted to work at. My top priority was a place where I would be challenged and continually grow. Above money, status, and even work environment, the most important consideration was always potential upside. But how to get there? I could have tried finding a master of the craft and acting as an apprentice, like letting the guru shed light where most people are left in the dark. That’s the path I took to become a derivatives trader, but programming is different. Programmers learn by building. And in five short months, build I have done. When I look back on 2013, I’ve come a long way. I’ve written code that handles the logic around fullfilment, payment providers and customer service. I’ve dealt with every major social media API out there. I’ve even produced client friendly HTML emails, which is the equivalent of the dark ages for coding. My proudest accomplishment is graduating from hacky coding to Test Driven Development. There’s so many positive things I can write about my experience at Walker and Co. Brands, but since this is a coding blog, the best thing is how I’ve developed my craft.

When new coders ask me for career advice, I can only point to my own experience. The best place for upside is the one where you can get your hands dirty. Forget structure or a paved path to follow. Go where there’s growth. Growth for you, growth for your team, and growth for your company. Here’s to hoping for an even better 2014!

Subdomains Kicking Your Butt (or Tests)?

Subdomains are unwieldy things. I naively thought in my rspec tests, I could write something like this: visit leads_url(subdomain: ‘squeeze’, host: 'lvh.me').

On inspecting leads_url(subdomain: ‘squeeze’, host: 'lvh.me').inspect, it looks correct, returning http://squeeze.lvh.me.

Running the test suite on my local machine, everything was green. I was patting myself on the back for brilliantly guessing the syntax, but when the tests were run on semaphore, a hosted continuous integration service for Ruby on Rails applications, things started falling apart. The tests in question were the ones with :js => true, and they were returning something like this:

1
2
3
4
5
6
7
8
9
Failures:

  1) sign up email via squeeze page sign up with email
     Failure/Error: find('#lead_email').set(lead_info[:email])
     Capybara::ElementNotFound:
       Unable to find css "#lead_email"
     # ./spec/integration/sign_up_spec.rb:47:in `lead_sign_up_as'
     # ./spec/integration/sign_up_spec.rb:40:in `lead_sign_up'
     # ./spec/integration/sign_up_spec.rb:19:in `block (2 levels) in <top (required)>'

Debugging technique: I added some debugging statements, mainly print page.html right after visit leads_url.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
feature 'sign up email via squeeze page' do
  scenario 'sign up with email', :js => true do
    lead_sign_up

    expect(page).to have_css 'h2.thank-you', text: 'THANK YOU FOR SIGNING UP'
    expect(page).not_to have_css 'form.squeeze-form'
  end

def lead_sign_up
  lead_sign_up_as({email: Faker::Internet.email, first_name: 'Joe', last_name: 'Schmoe'})
end

def lead_sign_up_as(lead_info = {})
  first_squeeze_campaign = FactoryGirl.create(:campaign)
  squeeze_url = squeeze_url(subdomain: first_squeeze_campaign.subdomain, host: 'lvh.me')
  visit squeeze_url

  print page.html  # <------ debugging statement

  find('#lead_email_medium').set(lead_info[:email])
  find('#lead_first_name_medium').set(lead_info[:first_name])
  find('#lead_last_name_medium').set(lead_info[:last_name])

  find('#lead-form_medium input.submit').click
end

Rerunning the test suite on semaphore, tests with js: true, gave me this:

1
2
3
sign up email via squeeze page
"<html><head></head><body></body></html>"
  sign up with email (FAILED - 1)

No wonder my tests were complaining that they couldn’t find fill out the form and the selector couldn’t be found! There was nothing on the page. Once I removed the :js => true, the page would miraculously appear again. What was going on?

It turns out that Capyabara drivers, like Poltergeist, needs to run against an actual HTTP server. Capybara starts one for you in the same test, but on a different thread. I’m not going to claim to actually understand this, but this would explain why when :js => true is left out, the tests pass.

Solution

Specifiying the Capybara’s app host is crucial.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
feature 'sign up email via squeeze page' do
  scenario 'sign up with email', :js => true do
    lead_sign_up

    expect(page).to have_css 'h2.thank-you', text: 'THANK YOU FOR SIGNING UP'
    expect(page).not_to have_css 'form.squeeze-form'
  end

def lead_sign_up
  lead_sign_up_as({email: Faker::Internet.email, first_name: 'Joe', last_name: 'Schmoe'})
end

def lead_sign_up_as(lead_info = {})
  first_squeeze_campaign = FactoryGirl.create(:campaign)
  squeeze_url = squeeze_url(subdomain: first_squeeze_campaign.subdomain, host: 'lvh.me')
  Capybara.app_host = squeeze_url    # <--------- new addition
  visit squeeze_url

  find('#lead_email_medium').set(lead_info[:email])
  find('#lead_first_name_medium').set(lead_info[:first_name])
  find('#lead_last_name_medium').set(lead_info[:last_name])

  find('#lead-form_medium input.submit').click
end

Also added this new line to spec_helper.rb.

1
Capybara.always_include_port = true

Voila! We are back in the green! I should mention that I was banging my head against the wall for several days on this one.

These two stackoverflow threads led me in the right direction, Capybara with subdomains default host and Testing javascript on subdomain with capybara, phantomjs and rails

Lastly, my setup.

  • phantomjs (1.9.2)
  • capybara (2.1.0)
  • poltergeist (1.4.1)
  • ruby 1.9.3p448
  • OSX 10.9

If anyone can answer why this was working locally and not on semaphore, I’m all ears!

The Technology Mecca

For someone that prefers to eat the same thing for lunch everyday, I have not escaped the inevitable part of our lives, constant change. Today, marks my second week of living in San Francisco. Yes, I have moved from my beloved New York City, to join the hordes of technology centric people in the Bay.

About two months ago, a colleague and friend of mine from a previous life sent me a tweet that initiated our conversation about his new startup. He wanted me to come on board. Joining a startup, at that point, was starting to sound like a far fetched dream. At thirty years old (gasp!), could I truly muster the energy and right dose of naiivete to devote most of my waking hours to a startup? After years of trading volatility for a living, I craved stability.

But when opportunity slaps you in the face, even I can’t turn away. I wanted to work in a small team, influence a company’s direction, and grow something from nothing. This was all of that. So here I am, working for a startup in Palo Alto, living my far fetched dream.

Getting Comfy With Bash Commands

With Sublime, GitX, SourceTree, Filezilla, and good old Finder, why bother with the terminal? Give me a GUI with beautiful design. Hasn’t programming evolved to be accessible for everyone?

But sooner or later, you’ll need to master the terminal. For me, it became glaringly apparent when I wanted to move out of the confines of the usual git commit, rails s, rake db:migrate. The day came that I needed to ssh into our server, and figure out why my production environment was not at all like my development environment.

For the past two weeks, I’ve been working with one of my co-workers with years of system administration experience. Here’s a list of useful bash commands he’s bestowed upon me, and yes, I’m sticking in a git command as well.

!$ : last string, from the last command
echo "hello" >> ./config/newrelic.yml : appends string to the end of file
echo "hello" > ./config/newrelic.yml : replaces whole file with the string
echo: like a ruby puts statement for variables
ctrl + c: clear
ctrl + r: reverse search
sudo chown user_name directory_name: change owner of directory
git branch --merged | grep -v "*" | xargs -n 1 git branch -d: deletes git branches that are already merged. Make sure you’re in master before you run.
sudo ln -s /usr/local/share/whatever/name_of_file /usr/local/bin: creates a soft symlink
mv /path/to/file /path/to/filedate '+%m%d%y%H%S': timestamps a file
tar -czvf file_directory.tgz ~/path/to/directory: creates tar file
diff test1.txt test2.txt: compares the diff between two files.

Geeking Out

I always secretly knew I was a geek. It might have something to do with the math and science classes that I was inherently more interested in back in school. Or maybe it was because of the thick glasses I wore up until high school that clued me in. Little did I know the nerd chic would be a thing. Didn’t see that one coming. Plus, I’m a dancer, a loud laugher, and as quick tempered as they come. So like a good Asian American girl, deny, deny deny. No, I can’t be a nerd like the rest of the asians Hollywood depicted. But it’s come to my attention that I can’t hide it anymore. Last week, I had the honor of being attending Google I/O 2013 conference and of course, there was some swag to be had.

With my new toy sparkling in my mind’s eye, I spent the morning looking up ways to run Ubuntu on my new Pixel Chrome Book. With glee, I carefully saved each web article into my well labeled Evenote system. Reading through every post at work, I skipped home to transform my Pixel into my main development box. In order to get around the fact that the Pixel gives you limited terminal access and your favorite text editor isn’t supported, you can boot the machine up in developer’s mode.

After careful consideration, I decided to go the route of using an Ubuntu chroot. Crouton, Chromium OS Ubuntu Chroot Environment, allows me to have both the Chrome OS and Ubuntu OS up at the same time. This way I can toggle between the two. However, it’s not a true Ubuntu virtual machine. On the flip side, Crouton saves me much needed memory space. Thanks to Brenton Simpson and his script, I am ready to rock and roll on my new Pixel!

For those of you new to Ubuntu machines, here are some helpful tips:

  1. Copy and Paste shortcut keys: Ctrl + Shift + C Ctrl + Shift + V
  2. If Vim isn’t your thing, try nano. sudo apt-get install nano
  3. When you add-apt-respository, don’t forget to apt-get update.
  4. This isn’t specific to Ubuntu, but if you ever find that you need to go to the beginning of the line to edit something, and the left arrow is just too slow, Ctrl + A will get you to the front of the line. Ctrl + E gets you back to the end.
  5. And lastly, here’s a great article with more Terminal shortcuts that every developer needs to know.

Learning About SSL the Hard Way: Recent RVM Installation Error

Does this error look familiar? If you happen to be reinstalling RVM onto a new computer or virtual machine, this error might have popped up. So what gives? My co-workers, @jonzeper and @ctruelson, figured this one out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[2013-03-15T15:42:22+00:00] INFO: Processing rvm_default_ruby[ruby-1.9.3] action create (rvm::system line 183)

================================================================================
Error executing action `create` on resource 'rvm_default_ruby[ruby-1.9.3]'
================================================================================

NoMethodError
-------------
private method `chomp' called for nil:NilClass

Cookbook Trace:
---------------
/tmp/vagrant-chef-1/chef-solo-1/cookbooks/rvm/libraries/chef_rvm_string_cache.rb:77:in `canonical_ruby_string'
/tmp/vagrant-chef-1/chef-solo-1/cookbooks/rvm/libraries/chef_rvm_string_cache.rb:50:in `fetch'
/tmp/vagrant-chef-1/chef-solo-1/cookbooks/rvm/libraries/chef_rvm_string_helpers.rb:55:in `normalize_ruby_string'
/tmp/vagrant-chef-1/chef-solo-1/cookbooks/rvm/providers/default_ruby.rb:26:in `load_current_resource'

Resource Declaration:

---------------------
# In /tmp/vagrant-chef-1/chef-solo-1/cookbooks/rvm/libraries/chef_rvm_recipe_helpers.rb

182:         # set a default ruby
183:         rvm_default_ruby opts[:default_ruby] do
184:           user  opts[:user]
185:         end
186:

Compiled Resource:
------------------
# Declared in /tmp/vagrant-chef-1/chef-solo-1/cookbooks/rvm/libraries/chef_rvm_recipe_helpers.rb:183:in `install_rubies'

rvm_default_ruby("ruby-1.9.3") do
  retry_delay 2
  retries 0
  recipe_name "system"
  ruby_string "ruby-1.9.3"
  action :create
  cookbook_name :rvm
end

[2013-03-15T15:42:22+00:00] ERROR: Running exception handlers
[2013-03-15T15:42:22+00:00] ERROR: Exception handlers complete
[2013-03-15T15:42:22+00:00] FATAL: Stacktrace dumped to /tmp/vagrant-chef-1/chef-stacktrace.out
[2013-03-15T15:42:22+00:00] FATAL: NoMethodError: rvm_default_ruby[ruby-1.9.3] (rvm::system line 183) had an error: NoMethodError: private method `chomp' called for nil:NilClass

Your computer connects to https://rvm.io. As of 3/15/13 at 1:20pm, the site has an expired SSL certificate! You can proceed anyways, but this error prevents your computer from automatically downloading and installing. No worries! There is a fix until @wayneeseguin updates their SSL certificate.

If you’re using vagrant, edit your cookbooks/rvm/attributes/default.rb file. There should be some lines that look like this:

1
2
3
4
5
# system-wide installer options
default['rvm']['installer_url'] = "https://get.rvm.io"
default['rvm']['branch']  = "stable"
default['rvm']['version'] = "head"
default['rvm']['upgrade'] = "none"

Change it to this:

1
2
3
4
5
# system-wide installer options
default['rvm']['installer_url'] = "http://get.rvm.io"
default['rvm']['branch']  = "stable"
default['rvm']['version'] = "head"
default['rvm']['upgrade'] = "none"

Ok, but what does SSL even mean? Why do we need SSL certificates? I recently ran into this issue because the native Android web browser didn’t like our SSL certificate. So this is not just an RVM problem. Here’s an oldie, but goodie from youtube explaining it in detail.

The Fashion Hackathon: Lessons Learned

Sponsored by Condé Nast, this hackathon was about bringing fashion together with hackers. I did not officially join a team, but was able to help contribute code during the wee hours of the night. Not bad for my first hackathon! Plus, what went down this weekend was a microcosm of an early stage startup: hash out an idea, build the product, pitch and sell!

For the newbie coders contemplating if they should sign up, here are my takeaways.

  1. People need you. I don’t care how hesitant and insecure you are about your skill level. You are desired. Keep in mind people are building prototypes in 36 hours, nothing fancy. Even a newbie can contribute, have fun and learn.
  2. Meet your community. Ever wonder where the passionate tech people with entrepreneurial minds hang out? Hackathons. Networking? Do it there. Single and looking? I know someone who met his girlfriend at a hackathon. Yup, hackathons are not just for dudes. 40% of participants at Decoded Fashion Fashion Hackathon were women.
  3. Do something new. Feeling bogged down at your job? Sick of tweaking that website for the billionth time? Don’t let those feelings chip away at your passion for coding. Hackathons get your adrenaline pumping and remind you how awesome it is to build.
  4. Assemble a team beforehand. Coding with people you have worked with gives you a tremendous advantage.
  5. Try to tackle the problem and brainstorm solutions beforehand. The first team I joined went through three ideas and disbanded after 4 hours because we couldn’t move forward with anything.
  6. Bring snacks/water/comfortable clothes. Even if they provide food, it’s usually not enough or unhealthy. You may or may not sleep that night.
  7. Practice that pitch! Teams had 2 minutes to present. You should present the problem, the concept of the solution, and quickly demo.
  8. Use the API’s the hackathon are promoting. It’ll make your app snazzier and you could win cool swag. Plus, I think the judges are looking for it.

Seeing how much fun I had, this won’t be my last hackathon.

Landing the Job

Source: etsy.com via Miya on Pinterest

Tomorrow, I start my first job as a software engineer at Conde Nast. I’ll be part of a small, independent team within the company, building a brand new online video platform. It’s promised to attract high traffic almost from day one. Needless to say, I’m thrilled with the opportunity and what’s to come for my own coding skills. And yes, they are still looking for talent. Email me at li.ouyang@gmail.com for more details.

For those of you who have read my previous post, How I Learned to Code in 8 Months, you may remember that I knew next to nothing before I started this journey. It just goes to show how far determination, time and commitment can take you. Good luck to everyone in their own endeavors!