<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>zachsteiner.com - Portfolio</title>
<link>https://zachsteiner.com</link>
<description>Portfolio updates from Zach Steiner.</description>
<lastBuildDate>Mon, 09 Feb 2026 22:00:32 GMT</lastBuildDate>
<docs>https://validator.w3.org/feed/docs/rss2.html</docs>
<generator>https://github.com/jpmonette/feed</generator>
<language>en</language>
<copyright>All rights reserved 2026, Zach Steiner</copyright>
<item>
<title><![CDATA[Stratus Design System]]></title>
<link>https://zachsteiner.com/portfolio/2021-04-01-stratus</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2021-04-01-stratus</guid>
<pubDate>Thu, 01 Apr 2021 00:00:00 GMT</pubDate>
<description><![CDATA[Design system component library]]></description>
<content:encoded><![CDATA[<p>When I joined JumpCloud in 2019, the engineering team was rapidly expanding. With many new engineers working at a fast pace, the interface started to become inconsistent. New features were implemented with a combination of older versions of the brand, design directions that had been abandoned, and one-off ideas developed in isolation without consideration for extensibility. In fall 2019, I led a hackathon project with a team of engineers and designers to create a proof of concept design system leveraging <a href="https://storybook.js.org/">Storybook</a>. After the hackathon concluded, both UX and engineering identified that building out the design system could prove useful, so I took on design system work and Storybook integration as a 10% project. After 4 months, the project was successful enough that I was put on the project full time. Leadership recognized the value of the design system and I was given additional engineering resources. My team grew to 3 engineers and a QA engineer with me as technical lead.</p>
<p>Now that the library is more fully integrated into the platform, we're experiencing increased demand for components. Though I wrote most of the foundational components for the library, my role has shifted. I am still actively contributing components, but with a new focus on documentation and providing strategic guidance to a growing number of UI engineers on feature teams. This includes bolstering documentation, working with designers on choosing patterns for new features, conducting training sessions, pair programming with engineers, and building subject matter experts throughout the engineering organization.</p>
<h2>Libraries</h2>
<p>The component library piece of the Stratus Design System is a private npm library built in Vue.js to support the web applications in JumpCloud's platform. A common color library serves as the source of truth for the UI color palette for a variety of development platforms: SCSS, custom properties, and JSON. The color logic and export file generation is written in Node.js.</p>
<p>The component library Storybook documentation is publicly available at <a href="https://design.jumpcloud.io">design.jumpcloud.io</a>.</p>
<h2>Integrations</h2>
<p>My team has integrated the component library into three applications to date. The UIs of two were entirely refreshed to use the new components to improve the UX, responsiveness, and accessibility. The third application, the administrator portal, was too large and complex to be refreshed quickly, and would require a solution that supported a mix of new and legacy UI.</p>
<p>Refreshing the administrator portal was going to be a tremendous undertaking for product and engineering, and was blocked by a big piece of technical debt that made mixing legacy and new components impossible. The legacy application had a custom pixel <code>font-size</code> set on the <code>html</code>, while the new UI components were built with rem units assuming browser default. This caused new UI components to rendered incorrectly when used on legacy pages.</p>
<p>The component library heavily leverages CSS custom properties, so I architected a solution to abstract all sizing into custom properties. For example, rather than setting padding in a component to <code>1.5rem</code>, I set it to <code>var(--size-1p5)</code>. The custom property is set to <code>1.5rem</code> by default and then <code>24px</code> when a legacy class is added to the html element in the application. For example, this allows the components to be used on a page where the rem is set to <code>14px</code>. This change required refactoring all of the component sizing, necessitating an extensive regression test effort that involved both the design team and my team's QA engineer. The effort was justified as it enabled new UI components to be used in any part of the application. This has allowed broader adoption of the design system in the administrator portal.</p>
<h2>Components & Documentation</h2>
<p>New UI components are built with composability and flexibility in mind. Semantic color names and meta variables like <code>--jc-border</code> or <code>--jc-input-padding</code> are combined into tokens to ensure ease of use for contributors and maintain consistency. Many components leverage <a href="https://v2.vuejs.org/v2/guide/components-slots.html">Vue slots</a>, so custom implementations are possible. For example, a confirmation modal can have a custom footer instead of the default button group. This flexibility supports variations without requiring a separate component for each use case. By using these style tokens and Vue slots, developers can even compose one off UI elements within other applications that are aligned with the look and feel of the new UI components.</p>
<p>This approach is used within the library to build complex components like ItemListPagination, which is built using the <a href="https://bradfrost.com/blog/post/atomic-web-design/">atomic design framework</a>. At its lowest level, it uses style tokens like colors, borders, and spacing. It also leverages atoms like checkboxes, icon buttons, and select list. Finally, it uses molecules like the pagination bar and an optional toolbar that can be added via a Vue slot. Each list renders a row component unique to the list that is linked to the header via configurable grid layout. All of the methods for sorting and pagination are <a href="https://vuejs.org/guide/components/events.html">emitted events</a>. Ultimately the component is presentational, only rendering what its parent business logic and data controls, allowing the component to be independent of a particular data model or implementation.</p>
<p><img src="/images/portfolio/stratus/stratus7.png" alt="Item List"></p>
<p>For components that require extensive configuration like Tabs or ItemListPagination, I wrote documentation pages with code samples. To make these components even easier to use, I wrote wrapper components in administrator portal to reduce boilerplate code.</p>
<p><img src="/images/portfolio/stratus/stratus5.png" alt="Components"></p>
<p>The documentation has evolved as teams use the system in feature work, revealing gaps or points of confusion. For instance, observing usage of use Grid components, I wrote documentation on how, when, and, importantly, when not to use the Grid components. I used real life examples of designs and provided suggestions on how to implement them using the library.</p>
<p><img src="/images/portfolio/stratus/stratus4.png" alt="Components"></p>
<h2>Testing</h2>
<p>To ensure quality, new UI components are covered by unit tests written in Jest and manually tested in Storybook. Storybook is great for exercising and previewing components, but the implementation is somewhat artificial, particularly for form elements. To more thoroughly cover the component library, I configured an example Vue application with a suite of functional tests written in <a href="https://www.cypress.io/">Cypress</a>. It lives in the repository, but imports the library like other consuming applications. A Cypress test suite runs in CI to catch issues in important interactive components when new changes are introduced. For instance, there is an end-to-end test of form behavior. To test data binding for all form element types it prints values on the page, checks that validation behaves as expected, and verifies that a success notification is dispatched on save. This protects against regressions when refactoring or updating existing components.</p>
<p><img src="/images/portfolio/stratus/stratus9.png" alt="Example App Cypress"></p>]]></content:encoded>
<author>Zach</author>
<category>JavaScript</category>
<category>Vue.js</category>
<category>Node.js</category>
<category>Design Systems</category>
<category>Style Guide</category>
</item>
<item>
<title><![CDATA[Stratus Color Tools]]></title>
<link>https://zachsteiner.com/portfolio/2021-03-01-stratus-colors</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2021-03-01-stratus-colors</guid>
<pubDate>Mon, 01 Mar 2021 00:00:00 GMT</pubDate>
<description><![CDATA[Color tools]]></description>
<content:encoded><![CDATA[<p>I built a suite of custom color tools to support the <a href="/portfolio/stratus">Stratus design system</a> that built on and refined my work in <a href="/portfolio/tenderwolf">Tenderwolf</a>. These tools help both designers and engineers create more accessible and consistent UIs.</p>
<p>Colors are defined in a JSON file with name, hex value, variable name, and an array of color values (i.e., tints and shades). The tool generates the values of the color using a mixture pattern based on a <a href="https://material.io/design/color/the-color-system.html#color-usage-and-palettes">Material Design's color framework</a> to create lighter and darker variants of the base color (500 value). Tints (values under 500) mix white into the base color. Shades (over 500) mix a darkened version of the color into itself. This provides consistency in the color palette, as all the values have a the same relationship to the base color. Knowing that 50 is consistently lightest and 900 the darkest makes the colors easy to use, particularly in the grey scale.</p>
<p>To add greater flexibility for designers, the tool can override a color value with a specific hex code for a shade or tint. For example, the generated 900 value did not have sufficient contrast to be used against a white background, so we manually set it to a darker value.</p>
<pre><code class="language-javascript">{
name: 'Sky Blue (Informational)',
variable: 'jcInformational',
hex: '#afcbff',
colorValues: [50, 100, 200, 300, 500, 700, 900],
colorValueOverrides: {
900: '#4373c7',
},
}
</code></pre>
<h2>Color Chips</h2>
<p>The component library has a color chip component that displays metadata for the color and provides WCAG AA color contrast ratios for white and black.</p>
<p><img src="/images/portfolio/stratus/stratus2.png" alt="Color Chips"></p>
<p>The data driven approach has already paid off as JumpCloud has iterated its brand color palette since establishing the UI color palette. Not only did we need to update base colors, but the overall mixture percentage of white for tints (values under 500).</p>
<h2>Color Contrast</h2>
<p>To facilitate better design and engineering alignment on WCAG color contrast, I built a contrast tool that allows any two values of colors in the palette to be compared. The tool takes into account both color and type size to ensure compliance with WCAG guidance for large and small text. For example, a color may meet AA accessibility for the "Heading Huge" size, but not "Body Text".</p>
<p><img src="/images/portfolio/stratus/stratus3.png" alt="Contrast Tool"></p>
<h2>Legacy Variables</h2>
<p>To aid adoption and accelerate deprecation of legacy color variables, I wrote a variable mapping tool to make it easy for developers to search for legacy color variables and find a replacement in the new palette. The list of legacy and new UI color mappings are specified in a JSON file for easy maintenance.</p>
<p><img src="/images/portfolio/stratus/stratus10.png" alt="Legacy Variables"></p>]]></content:encoded>
<author>Zach</author>
<category>JavaScript</category>
<category>Vue.js</category>
<category>Node.js</category>
<category>Design Systems</category>
<category>Style Guide</category>
</item>
<item>
<title><![CDATA[Custom Email Editor]]></title>
<link>https://zachsteiner.com/portfolio/2021-01-20-custom-email</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2021-01-20-custom-email</guid>
<pubDate>Wed, 20 Jan 2021 00:00:00 GMT</pubDate>
<description><![CDATA[Custom email editor]]></description>
<content:encoded><![CDATA[<p>Customers raised an important issue with the product's system emails (e.g., user welcome, password reset, etc.) Though administrators could add minimal branding, their end users were confused by emails and often reported them as phishing. Users did not have name recognition of JumpCloud and would ignore emails for password resets or device lockouts. To solve this issue of end user trust, my team built tools to customize the content of key system emails, allowing administrators to customize the message and look so end users could better identify emails and trust the content.</p>
<p>I collaborated with a designer on the UX and built the UI, while my teammates built a new endpoint and updated the email build process to accommodate custom emails.</p>
<h2>Design & Constraints</h2>
<p>We conducted research with customers to get feedback on three design concepts including:</p>
<ol>
<li>separate inputs for each piece of customizable part of the email</li>
<li>a rich text editor to write the whole email</li>
<li>an advanced editor that allowed writing custom HTML code</li>
</ol>
<p>Customers liked the flexibility of the rich text editor, but also appreciated the simplicity of individual fields. Customers agreed that hand editing HTML was a lot to expect, especially without a good way to test emails.</p>
<p>We discovered that sending a sample email on demand was not feasible with our email backend service. As an alternative, I suggested a live preview that would give a sense of how the email would look. Though the preview couldn't leverage the production CSS or email templates, we could align it as closely as possible.</p>
<p>Because the existing email templates required specific pieces of content in partials or styled elements, a rich text editor solution was unfeasible. For example, there are elements like call to action buttons that are partials that the user cannot customize. Given cross email client rendering issues rich text editing seemed risky absent an on-demand preview email. Finally, requiring IT administrators to debug cross email client issues is not user friendly. We solved these issues by separating the email into sections that the administrator can customize via inputs in the UI.</p>
<p>The existing system emails used variables, including organizational information like administrator contact email, as well as email specific data like the expiration date of a user's password. These were important pieces of information for end users and could not be sacrificed.</p>
<h2>Solution</h2>
<p>The interface has two panes. In the left editing pane, administrators choose a template. The form inputs are dynamically generated from the fields available in the template. The right panel updates to display a preview of the email in real time.</p>
<p><img src="/images/portfolio/customemail/customemail1.png" alt="Main Page"></p>
<p>To solve for variables in emails, we developed a set of tokens. Tokens are entered in the format <code>#{expiration_day}</code> and highlighted in the preview with a dotted underline. Invalid tokens display as plain text.</p>
<p><img src="/images/portfolio/customemail/customemail4.png" alt="Main Page"></p>
<p>The administrator can preview tokens via a toggle switch. Tokens that are organization specific, like organization name or administrator contact email, are real data. Email or user specific tokens, like a password expiration date or username, are previewed with mock data. Each token has a description in a tooltip to provide context while editing.</p>
<p>The token preview and tooltip posed a technical challenge. For the preview styling and tooltip, I had to render a custom Vue component in place of the token text. I wrote a function that deconstructed the text into an array, using regex to split the string at tokens. Tokens are validated against a list of possible tokens provided by the API, so only valid tokens are rendered with the preview style. Finally the component renders plain text in spans and valid tokens render as a Vue component.</p>
<p><img src="/images/portfolio/customemail/customemail3.png" alt="Main Page"></p>
<p>During development, internal users struggled to fully understand tokens. It seemed reasonable that administrators would have similar issues. Providing documentation in a knowledge base article was too removed from the task, so I designed and implemented a documentation tool that lives at the bottom of the page. The tool includes a list of tokens with a description and a preview value for each. The preview is important for tokens that don't render as simple text. For example, <code>#{admin_email_link}</code> renders a <code>mailto:</code> link with the administrator name as the text. Where relevant, the tool also provides a link to the setting where the token value can be updated.</p>
<p><img src="/images/portfolio/customemail/customemail2.png" alt="Main Page"></p>]]></content:encoded>
<author>Zach</author>
<category>JavaScript</category>
<category>Vue.js</category>
<category>UX</category>
</item>
<item>
<title><![CDATA[Tenderwolf]]></title>
<link>https://zachsteiner.com/portfolio/2019-04-16-tenderwolf</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2019-04-16-tenderwolf</guid>
<pubDate>Tue, 16 Apr 2019 00:00:00 GMT</pubDate>
<description><![CDATA[Data-driven style guide tool.]]></description>
<content:encoded><![CDATA[<p>Two of my client engagements with Buildit required a large scale front-end style guide and UI templating. I identified a good candidate in <a href="https://fbrctr.github.io">Fabricator</a>. It got me most of the way there with a flexible organization scheme and ability to build whole pages from mock data. Also it is very hackable. Across two projects, I pushed Fabricator to its limits and, with a front-end teammate, we partially rewrote a large section of its internal engine. See discussion on my <a href="/portfolio/ecommerce">Enterprise E-commerce project</a> At the end of the two engagements, I knew I needed something more to move forward.</p>
<h2>Limitations of Fabricator</h2>
<ol>
<li>No ability to define default data from a partial, so to have a good default display of a material, each template required a ton of if statements.</li>
<li>Could only define mock data with JSON, precluding <a href="https://github.com/Marak/faker.js">faker</a> or API data.</li>
<li>All mock data was shoved into a global store, but there was no way to view it as one might in Vuex or Redux.</li>
<li>Color palette specimen included little information about accessibility</li>
<li>Color palettes and other specimens (e.g., type, breakpoints) could not be shared with Scss</li>
<li>No icon system solution</li>
<li>No ability to leverage Handlebars layouts feature</li>
<li>The navigation of every material on a single page falls apart in large libraries such as the ones I built (think 100+ pages, 100s of materials)</li>
<li>Fabricator needs to be forked each time, so it was a pain to keep our custom version update to date across projects</li>
<li>No search, so finding materials got challenging</li>
</ol>
<h2>Tenderwolf</h2>
<p>We were able to add page navigation, building Sass variables, and more robust specimens to a forked Fabricator. However, other limitations were fundamental to how Fabricator works. I collaborated with my teammate <a href="https://github.com/zmetcalf">Zach Metcalf</a> to create a tool from the ground up. I served as product owner and UI designer/developer. Zach M handled the Webpack and Node side of the app.</p>
<p>After some experimentation with Vue.js and a variety of templating languages, we settled on a custom Webpack build process with Handlebars (kept from Fabricator).</p>
<p><img src="/images/portfolio/tenderwolf/tenderwolf-main.png" alt="Example Atoms"></p>
<h3>Mock Data</h3>
<p>We built Tenderwolf to be data agnostic, it can accept JS files or JSON files. This makes the sky the limit as far as mock data. Particularly the introduction of faker.js could have saved untold time I spent rolling my own JSON files.</p>
<p>Further, materials (atoms, molecules, etc.) now can have default data (think default props in Vue or React) defined in front matter. Consider a button, now the text can be set as a default rather than having a if statement. The downside is that we had to create a custom helper (include), rather than using the standard partial import syntax (which works in other situations).</p>
<pre><code class="language-handlebars">---
text: 'Button Text'
---
<button class="button">{{text}}</button>
/* Usage */
{{include atoms/buttons/button text="Page Wrap Button" }}
</code></pre>
<p>For debugging purposes, all data is readable in a console log during development.</p>
<p>To aid in working with data driven partials, Tenderwolf includes <a href="https://www.npmjs.com/package/just-handlebars-helpers">just-handlebar-helpers</a> which adds a slew of common templating features missing from core Handelbars, in particular inline ternary if statements, arithmetic functions, and more robust logical operators.</p>
<h3>Specimens</h3>
<p>Fabricator includes rudimentary color specimens built from JSON data, but my projects required a more robust data-driven approach. Based on JSON data in the project (see below), Tenderwolf calculates color values in alternative representations (i.e., RGB and HSL) from a Hex code and accessibility values for white and black text to meet WCAG AA requirements for large and small text. Further, tenderwolf pushes the hex code to a Scss variable partial that is accessible in the project. Unlike Fabricator, where you would need to define color variables twice, Tenderwolf allows the JSON to be the source of truth for documentation and coding.</p>
<pre><code class="language-json">[
{
"variable": "brand",
"value": "#007bff",
"name": "Brand",
"usage": "Main brand color. Used for links and buttons.",
"active": true
},
{
"variable": "white",
"value": "#FFFFFF",
"name": "White",
"usage": "",
"active": true
},
{
"variable": "black",
"value": "#000000",
"name": "Black",
"usage": "",
"active": true
}
]
</code></pre>
<p><img src="/images/portfolio/tenderwolf/tenderwolf-colors.png" alt="Colors"></p>
<p>Further, Tenderwolf also allows specification of specimens for typography (headings, paragraphs, etc.), borders, logos, and breakpoints. All are rendered on the styles page and pushed to Sass variables for use in the project. This is relatively extensible, so other specimens could be created in the future. Additionally, arbitrary Sass variables can be defined in the project, if so desired.</p>
<p><img src="/images/portfolio/tenderwolf/tenderwolf-type.png" alt="Typography"></p>
<h3>Icons</h3>
<p>Tenderwolf builds a directory of SVG files into an optimized sprite file. For browser support, <a href="https://github.com/jonathantneal/svg4everybody">svg4everybody</a> is included. Icons can be accessed through the <code><use></code> pattern. A library of all icons is rendered on the styles page.</p>
<pre><code class="language-html"><svg class="icon icon--inline">
<use xlink:href="/svg-defs.svg#icon-chevron-left"></use>
</svg>
</code></pre>
<h3>Layouts</h3>
<p>Tenderwolf supports multiple layouts using Handlebars layout partials, for example:</p>
<pre><code class="language-handlebars">---
title: "Sample Page"
mockLink: "http://google.com"
---
{{!-- Uses base.html layout in /src/layouts --}}
{{#> base }}
{{#*inline 'header-block'}}
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.14/lodash.core.min.js'></script>
{{/inline}}
{{#*inline 'content-block'}}
<h1>
Sample Page.
</h1>
{{#> organisms/cards/card }}
<h1>Page Wrap</h1>
{{include atoms/buttons/button text="Page Wrap Button" }}
{{/organisms/cards/card}}
{{/inline}}
{{/base}}
</code></pre>
<p>This allows flexibility of both layout and includes for pages.</p>
<h3>Navigation to Materials</h3>
<p>Tenderwolf borrows the directory driven navigation from Fabricator, but extends its organization to be more flexible for large projects. Materials directories can have as many sub-levels as needed:</p>
<p><img src="/images/portfolio/tenderwolf/tenderwolf-directory.png" alt="Directory Structure"></p>
<p>For instance, Atoms / Buttons / Button Links will render its own page.</p>
<p><img src="/images/portfolio/tenderwolf/tenderwolf-nav.png" alt="Navigation"></p>
<p>The top level navigation reflects the directories in the materials directory (our starter uses standard <a href="http://bradfrost.com/blog/post/atomic-web-design/">Atomic Design</a> categories. If one were to do a scaled back Atomic Design or entirely different organizational scheme, Tenderwolf can support it.</p>
<p>We also implemented basic name search to more easily find materials in large projects.</p>
<p><img src="/images/portfolio/tenderwolf/tenderwolf-search.png" alt="Navigation"></p>
<h3>Separation of Concerns</h3>
<p>The core CLI build tool for Tenderwolf lives in a separate repo from individual projects, so that the CLI can be updated and version independent of projects that use it. We created a starter project to speed development, so the CLI tool will be imported as a NPM library rather than having to fork the entire repo as with Fabricator.</p>
<p>The separation posed some challenges around error handling for mock data. Initially we assumed all specimen data would exist in the project, but once I started deleting JSON data, the build breaks. I went through and hardened the CLI to have sensible defaults and fallbacks when specimen data is not available.</p>
<h2>Next Steps for Tenderwolf</h2>
<p>I hope to open source Tenderwolf for interested parties. There will need to be some additional hardening, finishing, and documentation work to get it ready for primetime.</p>]]></content:encoded>
<author>Zach</author>
<category>JavaScript</category>
<category>UX</category>
<category>Design Systems</category>
<category>Style Guide</category>
</item>
<item>
<title><![CDATA[Album Art]]></title>
<link>https://zachsteiner.com/portfolio/2019-03-01-album-art</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2019-03-01-album-art</guid>
<pubDate>Fri, 01 Mar 2019 00:00:00 GMT</pubDate>
<description><![CDATA[Get album art from iTunes and Spotify]]></description>
<content:encoded><![CDATA[<p>I built this application mainly to learn Vue.js while on the bench, but also to make it easier to get artwork for my local media library. I started pulling data from iTunes Store API, which was rather easy. Apple makes this API public and it's pretty straightforward to get the data into Vuex store. Filtering by media type was also relatively straightforward. This project also allowed some experimentation with CSS Grid for layout.</p>
<p><img src="/images/portfolio/albumart-large.png" alt="Album art"></p>
<p>Not all music is on iTunes however, so Spotify was the next logical data source.
Spotify was a bit more challenging. Spotify requires authentication to access
album information. The trickiest part of the capturing auth was maintain state
of search while the user goes out and authenticates. The store needs to juggle a
lot especially when pulling search from the URL query string and remembering
search when switching between iTunes and Spotify.</p>]]></content:encoded>
<author>Zach</author>
<category>JavaScript</category>
<category>UX</category>
<category>Vue.js</category>
</item>
<item>
<title><![CDATA[Weather]]></title>
<link>https://zachsteiner.com/portfolio/2018-11-15-weather</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2018-11-15-weather</guid>
<pubDate>Thu, 15 Nov 2018 00:00:00 GMT</pubDate>
<description><![CDATA[Experiment with weather visualizations.]]></description>
<content:encoded><![CDATA[<p><strong>Update</strong>: I removed the link to the live site as Dark Sky has been acquired by Apple and the API is no longer available. The code is still available.</p>
<p><img src="/images/portfolio/weather/weather1.png" alt="Main Forecast"></p>
<p>I built this application while on the bench between client engagements to get better at interaction with REST APIs and put <a href="https://reactjs.org/docs/context.html">React Context</a> through its paces. I also used it as an opportunity to experiment with some data driven CSS layouts.</p>
<p>Data comes from the <a href="https://darksky.net/dev">Dark Sky API</a>. The API request gets pushed into a context that can be accessed from children components, as an alternative to Redux. To ration API requests, I push the forecast object into local storage. On load there is a check that location hasn't changed since last pull and that last forecast update is less than 60 minutes ago. A manual refresh or location change will force an update of the forecast.</p>
<p>The temperature dial, inspired by a visualization on Apple Watch, changes color gradient end points (with some HSL computation) depending on the temperature of high and low. The position of the circle required some high school geometry that took way too long to retrieve.... The end result pushes the position and color to CSS custom properties:</p>
<pre><code class="language-javascript">import colorValue from './colorValue';
function hueCalc(temp) {
return temp > 28 ? '50%' : '80%';
}
export default function temperatureColors(
temperature,
temperatureHigh,
temperatureLow,
) {
const hueLow = colorValue(temperatureLow);
const colorLow = `hsl(${hueLow}, 85%, ${hueCalc(temperatureLow)})`;
const hueHigh = colorValue(temperatureHigh);
const colorHigh = `hsl(${hueHigh}, 85%, ${hueCalc(temperatureHigh)})`;
let markerColor = 'transparent';
if (temperature >= temperatureHigh - 0.5) {
markerColor = colorHigh;
} else if (temperature <= temperatureLow + 0.5) {
markerColor = colorLow;
}
let root = document.documentElement;
root.style.setProperty('--temp-low', colorLow);
root.style.setProperty('--marker-color', markerColor);
root.style.setProperty('--temp-high', colorHigh);
}
</code></pre>
<pre><code class="language-javascript">export default function temperaturePosition(
temperature,
temperatureHigh,
temperatureLow,
) {
if (temperature < temperatureLow) {
temperature = temperatureLow;
} else if (temperature > temperatureHigh) {
temperature = temperatureHigh;
}
const currentRange = temperatureHigh - temperatureLow;
const currentDifference = temperature - temperatureLow;
let currentPercentage = currentDifference / currentRange;
let currentAngle = currentPercentage * 180 - 180;
let root = document.documentElement;
root.style.setProperty('--current-position', `${currentAngle}deg`);
}
</code></pre>
<p>Background images are based on location search using <a href="https://unsplash.com/developers">Unsplash's API</a> to retrieve a random photo from the location. I also integrated <a href="https://docs.mapbox.com/api/search/">Mapbox's geocoding API</a> to power the location search bar.</p>
<h2>Hourly Forecast</h2>
<p>The expanded hourly forecast required a fair amount of math to dynamically size and place them. The hourly forecasts take into account the total range for the 7 day forecast to position the bars on relative to the overall high and low for the week. Then the length of each bar is computed based on the range within the day. A similar method is used for the temperature dot graph on the time machine feature.</p>
<p><img src="/images/portfolio/weather/weather2.png" alt="Hourly Forecast"></p>
<h2>Time Machine</h2>
<p>The time machine data from Dark Sky allows you to pull historic data or future forecast based on past averages. Since the entire day is known, I experimented with React graphing libraries to display the data. The temperature graph is a custom CSS driven visualization, using similar layout techniques to the hourly graphs.</p>
<p><img src="/images/portfolio/weather/weather3.png" alt="Time Machine"></p>
<p>The line charts use <a href="https://nivo.rocks/line/">Nivo Line</a> after having tried <a href="https://formidable.com/open-source/victory/docs/victory-chart/">Victory Charts</a>. I found Nivo easier to work with and style, though both are robust enough to use in production environments. Massaging the API data into chart form did require some data manipulation:</p>
<pre><code class="language-javascript">import getDate from './getDate';
export default function buildConditionData(
conditionList,
hourlyData,
timezone,
) {
let hourlyConditions = {};
conditionList.map(item => {
let specificConditions = [];
const conditionKey = item;
hourlyData.map(item => {
const conditionValue = item[conditionKey];
const timeValue = getDate(item['time'], timezone);
specificConditions.push({
x: timeValue,
y: conditionValue,
});
return specificConditions;
});
hourlyConditions[conditionKey] = [
{
id: conditionKey,
data: specificConditions,
},
];
return hourlyConditions;
});
return hourlyConditions;
}
</code></pre>
<p><img src="/images/portfolio/weather/weather4.png" alt="Graphs"></p>
<p>Next steps for this project are to tailor the UI and feature set for hiking and outdoor activities, particularly ones in remote areas. I'm going to collaborate with <a href="www.jackieglimp.com">Jackie Glimp</a> on branding and design for the refreshed app. I will be swapping Dark Sky for NOAA data, providing a mobile friendly view into their <a href="https://forecast.weather.gov/MapClick.php?lat=39.7464&lon=-104.9924&unit=0&lg=english&FcstType=graphical">fantastic hourly forecast data</a>, while also making the forecasts more discoverable for outdoors types.</p>]]></content:encoded>
<author>Zach</author>
<category>JavaScript</category>
<category>UX</category>
<category>React</category>
</item>
<item>
<title><![CDATA[Enterprise E-commerce UI Library]]></title>
<link>https://zachsteiner.com/portfolio/2018-10-31-ecommerce</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2018-10-31-ecommerce</guid>
<pubDate>Wed, 31 Oct 2018 00:00:00 GMT</pubDate>
<description><![CDATA[UI library for enterprise e-commerce re-platforming.]]></description>
<content:encoded><![CDATA[<p><img src="/images/portfolio/ecommerce/ecommerce-main.png" alt="Main Forecast"></p>
<p>This engagement was with a Fortune 500 business-to-business hardware company. The client selected <a href="https://www.sap.com/products/crm.html">SAP Hybris</a> to re-platform two disparate e-commerce sites. One was a publicly available site for small and medium-sized businesses and the other a closed site for enterprise customers. Though each had different user needs, workflows, and features, the client wanted a consistent experience in-line with their nascent branding standards.</p>
<p>My role was to develop a style guide / UI library to support the broader development effort (a large team based offshore). I worked closely with a product designer that created workflows and visual designs for the new sites. The two of us worked closely with client product owners to gather requirements, define the overall user experience, and validate interface ideas. We also collaborated with the internal UX team to contribute UI patterns to their broader redesign project.</p>
<p>I created responsive versions of layouts (using desktop mockups as reference) and did rapid prototyping of interactions/layouts for technical feasibility and browser support. There were cases where the product owner's initial concept was not executable in the tech stack or supported browsers, so I had to pitch compromise designs. I built demos of pages with realistic mock data (e.g., real product names, codes, prices, etc.) to test content-related edge cases.</p>
<h3>Atoms / Buttons</h3>
<p><img src="/images/portfolio/ecommerce/ecommerce-atoms.png" alt="Atoms / Buttons"></p>
<p>To guide the development process, I crafted a UI library with hundreds of components ranging from simple atoms (e.g., buttons and form inputs) to organisms (e.g., product carousels and table elements) to layouts (e.g., product configuration page sections). I wrote the CSS and jQuery for UI interactions used in the final product. I documented the markup in the style guide and mock HTML pages. Eventually, I built a mock HTML page for most major and many variants of pages, totally 150+.</p>
<p>Regularly, I cut a versioned release of the UI library for the development team. They imported the CSS and JavaScript from the library and used the HTML as a reference for developing JSP templates. Often I would need to refactor markup or CSS to accommodate the template structure in the Hybris application. After a section was developed and released to QA environment, I coordinated with the development team to make layout and experience adjustments.</p>
<h3>Shopping Cart</h3>
<p><img src="/images/portfolio/ecommerce/ecommerce-cart.png" alt="Shopping Cart"></p>
<h3>Product Configuration</h3>
<p><img src="/images/portfolio/ecommerce/ecommerce-config.png" alt="Config"></p>
<h2>Tech Stack</h2>
<p>I wrote the UI library in highly customized version of <a href="https://fbrctr.github.io">Fabricator</a>. I originally chose Fabricator because of its flexibility and extensibility. However, I didn't realize at the outset just how much customization and refactoring would be required to meet the project's needs.</p>
<p>Initially, I rewrote the specimen templates to have more robust information about colors (included alternate color formats and accessibility information) and typography (included custom HTML elements and class names). I added a specimen for breakpoints. This required mostly Handlebars templating work, but also adding a Gulp task for the color-related computations.</p>
<h3>Typography</h3>
<p><img src="/images/portfolio/ecommerce/ecommerce-type.png" alt="Typography"></p>
<h3>Breakpoints</h3>
<p><img src="/images/portfolio/ecommerce/ecommerce-breakpoints.png" alt="Breakpoints"></p>
<p>I liked the idea of defining styles in JSON for style specimens, but I wanted a single source of truth rather than duplicating values between JSON and SCSS. I added a Gulp task to share color and typographic values to Sass variables.</p>
<p>Fabricator only had a basic image pipeline with no icon management system. I added an icon build process that converted a directory of SVG files into a SVG def file that was inserted into the HTML files (for better browser support than an external file). I rounded out the asset pipeline beyond images to include favicons and custom fonts.</p>
<p>To link design and the UI library, I expanded the page template to accept a link to mockup (hosted on Zeplin) in front matter, which would display as a link on the page list.</p>
<p><img src="/images/portfolio/ecommerce/ecommerce-pages.png" alt="Pages"></p>
<p>Finally, I collaborated with a front-end colleague to make Fabricator more robust for the size of such a library by building individual pages for material directories as opposed to rendering the entire library on a single page. For example, <code>src/materials/atoms</code> and <code>src/materials/atoms/buttons</code> would distinct pages. I created a new breadcrumb navigation to give transparency into the organization of materials. We also made the JSON data and Handlebars template handling more fault tolerant and hot reloading more reliable. By the end of the project, there were far fewer cases that required restarting the dev server. Debugging was still challenging because error logs didn't provide enough information, this would be resolved on my second project with this customized instance.</p>
<p>After all that work to enhance and refine Fabricator, there were still some serious workflow issues that we couldn't quite resolve. This was the impetus to develop of <a href="/portfolio/tenderwolf/">Tenderwolf</a>, a more data-driven successor to Fabricator.</p>]]></content:encoded>
<author>Zach</author>
<category>JavaScript</category>
<category>UX</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Odyssey Creation Platform]]></title>
<link>https://zachsteiner.com/portfolio/2018-01-12-odyssey-platform</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2018-01-12-odyssey-platform</guid>
<pubDate>Fri, 12 Jan 2018 00:00:00 GMT</pubDate>
<description><![CDATA[Next generation writing and editorial platform.]]></description>
<content:encoded><![CDATA[<p>I was lead user experience and user interface designer on Odyssey's next generation writing platform. The goal of the new platform was make writing accessible for a wide audience. In addition to supporting the casual writer, the platform also supports an editorial staff and group of elite writers with a robust tool set for editing and management. Finally, the platform supports Odyssey's community-based creation model with community management and communication tools.</p>
<p>The heart of the platform is a block rich editor, based on <a href="https://draftjs.org">Draft.js</a>. To serve our very mobile user base, I designed the editor to work on devices from smart phones to tablets to desktops. The editor is more than just text: users can search and embed Giphy GIFs, embed Twitter or Instagram posts, add images/videos/sound. The platform provides a traditional article template as well as a template to create and format listicles (a popular format on Odyssey).</p>
<p>I collaborated with product management on product strategy and feature set, helping define the conceptual model for the publishing process, community structure, and elite creator workflow. I created wireframes, user flows, and high fidelity mockups for all aspects of the system. I prototyped the editor, administration, community, and management sections in React.js with mock data. I wrote the majority of production CSS to support the UI. I created a React UI component library and guided developers in connecting the in-code prototypes to live data. I conducted user observation sessions and surveys to make improvements to the writing experience. I conducted feedback sessions with business stakeholders and synthesized feedback to inform new features and refinements. Throughout the development process, I helped refine and streamline features and workflows in response to internal and external feedback to better meet our users' needs.</p>]]></content:encoded>
<author>Zach</author>
<category>Design</category>
<category>Development</category>
<category>UX</category>
</item>
<item>
<title><![CDATA[Odyssey Corporate Site]]></title>
<link>https://zachsteiner.com/portfolio/2017-04-01-odyssey-marketing</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2017-04-01-odyssey-marketing</guid>
<pubDate>Sat, 01 Apr 2017 00:00:00 GMT</pubDate>
<description><![CDATA[Refresh of Odyssey's corporate and marketing site.]]></description>
<content:encoded><![CDATA[<p>I collaborated with the Creative Director to craft a new corporate site. This site contains marketing, sales, and career information separate from <a href="https://theodysseyonline.com">theodysseyonline.com</a>. The business required that content easily updated, so I crafted a solution in Jekyll with <a href="https://www.siteleaf.com">Siteleaf</a> as a CMS. All sections are built from posts or collections, such that the content is abstracted from the layouts. This allows for easy changes or reordering of content. All interactivity uses CSS animation and HTML inputs, without need for JavaScript. This project allowed me to experiment with video backgrounds, animations, and non-JS interactivity. The scrolling screenshots were a particularly fun challenge. They are constructed with a phone frame image and extra long screenshots of the site that scroll with CSS transforms and animation.</p>
<p><img src="/images/portfolio/odyssey-marketing/new-site.png" alt="Redesigned Site"></p>
<h2>Previous Version</h2>
<p><img src="/images/portfolio/odyssey-marketing/old-marketing-site.jpg" alt="Previous Site"></p>]]></content:encoded>
<author>Zach</author>
<category>Design</category>
<category>Development</category>
<category>UX</category>
</item>
<item>
<title><![CDATA[Album Covers]]></title>
<link>https://zachsteiner.com/portfolio/2016-11-13-album-covers</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2016-11-13-album-covers</guid>
<pubDate>Sun, 13 Nov 2016 00:00:00 GMT</pubDate>
<description><![CDATA[Custom artwork for missing album art.]]></description>
<content:encoded><![CDATA[<p>I have a handful of albums in iTunes that don't have cover art and a few (namely some classical titles) that have really uninspiring artwork. I created a set of covers using typographic layouts, illustration, and found artwork or photography. It was an opportunity to experiment with a range of aesthetics and layout techniques.</p>
<h2>Bach: Piano Transcriptions & Trio Sonatas</h2>
<p>"Piano Transcriptions" is my first in the series. It's a compilation from my friend Ali, so it simply had a Bach portrait until now. I decided to replace the mediocre "Trio Sonatas" to match. I substituted stylized piano with stylized organ pipes.</p>
<p><img src="/images/portfolio/covers/original-cover-1.jpg" alt="Bach Original">
<img src="/images/portfolio/covers/album-cover-1.png" alt="Bach 1">
<img src="/images/portfolio/covers/album-cover-2.png" alt="Bach 2"></p>
<h2>Beethoven: Trios</h2>
<p>The original is pretty uninspired. Given the recordings were in the late 1920s, I took some cues from Futurism for two variants.</p>
<p><img src="/images/portfolio/covers/original-cover-2.jpg" alt="Beethoven Original">
<img src="/images/portfolio/covers/album-cover-4.png" alt="Beethoven 1">
<img src="/images/portfolio/covers/album-cover-3.png" alt="Beethoven 2"></p>
<h2>Prince Bootlegs and Rarities</h2>
<p>These are covers for a few bootlegs and rarities from my Prince collection. Crystal Ball and Dream Factory are unreleased albums from the late 80s. The Truth is an "acoustic" album bundled as a bonus disc with the box set Crystal Ball (not the same as unreleased album). Crystal Ball features a lovely microscopic image of crystals. The Truth is a riff off a underwhelming unofficial cover.</p>
<p><img src="/images/portfolio/covers/album-cover-12.png" alt="Prince Original">
<img src="/images/portfolio/covers/album-cover-13.png" alt="Prince">
<img src="/images/portfolio/covers/album-cover-14.png" alt="Prince"></p>
<h2>Ives: Central Park in the Dark</h2>
<p>This is an orphan with the Symphonies broken out. I found a lovely turn of the century of Central Park (probably not in the dark).</p>
<p><img src="/images/portfolio/covers/original-cover-3.jpg" alt="Ives Original">
<img src="/images/portfolio/covers/album-cover-5.png" alt="Ives"></p>
<h2>Stravinsky: Rite of Spring</h2>
<p>The recording is from the mid 1980s, so the art could use some updating. I did play around with a variant featuring a Patrick Nagel illustration to go full 80s (think Duran Duran's Rio), but alas it's a proprietary image.</p>
<p><img src="/images/portfolio/covers/original-cover-4.jpg" alt="Stravinsky Original">
<img src="/images/portfolio/covers/album-cover-7.png" alt="Stravinsky"></p>
<h2>Takemitsu: Music of Takemitsu</h2>
<p>I've had this CD since the late 90s and always hated the art. I did two variants with different woodblock prints.</p>
<p><img src="/images/portfolio/covers/original-cover-5.jpg" alt="Takemitsu Original">
<img src="/images/portfolio/covers/album-cover-8.png" alt="Takemitsu">
<img src="/images/portfolio/covers/album-cover-9.png" alt="Takemitsu"></p>
<h2>Lee Konitz Live Albums</h2>
<p>Three Lee Konitz live albums that had either no cover or very awkward covers.</p>
<p><img src="/images/portfolio/covers/album-cover-konitz1.png" alt="Konitz 1">
<img src="/images/portfolio/covers/album-cover-konitz2.png" alt="Konitz 2">
<img src="/images/portfolio/covers/album-cover-konitz3.png" alt="Konitz 3"></p>
<h2>Les Catacombes, Paris 1969</h2>
<p>Live recording without a cover featuring Chick Corea, John McLaughlin, Dave Holland, and Jack DeJohnette. Two variants playing around with 1960s styles.</p>
<p><img src="/images/portfolio/covers/album-cover-11.png" alt="Les Catacombes">
<img src="/images/portfolio/covers/album-cover-10.png" alt="Les Catacombes"></p>
<h2>Saxophone Special, Jazz in der Kammer 1978</h2>
<p>Live recording without a cover featuring Peter Brötzmann, Evan Parker, John Tchicai, Steve Lacy.</p>
<p><img src="/images/portfolio/covers/album-cover-15.png" alt="Saxophone Special"></p>
<h2>Compositions & Improvisations</h2>
<p>This is my new artwork for my catch all album of personal recordings.</p>
<p><img src="/images/portfolio/covers/album-cover-6.png" alt="Compositions & Improvisations"></p>]]></content:encoded>
<author>Zach</author>
<category>Design</category>
<category>Typography</category>
</item>
<item>
<title><![CDATA[Hundred Proof]]></title>
<link>https://zachsteiner.com/portfolio/2016-01-03-hundred-proof</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2016-01-03-hundred-proof</guid>
<pubDate>Sun, 03 Jan 2016 00:00:00 GMT</pubDate>
<description><![CDATA[Math for the modern mixologist.]]></description>
<content:encoded><![CDATA[<p><img src="/images/portfolio/hundredproof-dilute.png" alt="Hundred Proof Scaling"></p>
<p>I made limoncello for the first time this holiday. This required some (not very fancy) math to figure out how to get the final product to a manageable proof. I didn't want my family too drunk at dessert nor did I want too weak a liqueur. I then had a conversation with a friend about how to serve 120 proof bourbon. It's a bit too strong and needs a touch of water. The art of making a cocktail is often the proper dilution, which is why ice is the most underrated ingredient in many cocktails. With small or large quantities, this can be tricky math with odd fractions of an ounce or the imprecise "drop". I found a useful <a href="http://redwhiteandbourbon.com/2013/03/27/how-to-cut-high-proof-bourbon-and-why-you-should-consider-it/">formula</a> that provides an amount of water to get an uncut bourbon to a desired proof for sipping, but it involved algebra and conversion of ounces to teaspoons. Even basic algebra is a lot to ask of even sober adults, so I thought this a good task for a web app. This formula forms the backbone of Hundred Proof.</p>
<p>For the time being, Hundred Proof is a basic dilution calculator, but I hope to build this out to a more full-featured mixology calculator. There are a few parts of the experience that make it more than a basic calculator, however. Users can seamlessly swap between ABV and Proof, and values are updated. Users can swap between shots, jiggers, and cups (for larger batches). The results are presented in fluid ounces and teaspoons and switch to cups for larger quantities.</p>
<p><img src="/images/portfolio/hundredproof-scale.png" alt="Hundred Proof Scaling"></p>
<p>I collaborated with the talented <a href="http://jackieglimp.com">Jackie Glimp</a> to make Hundred Proof. Jackie made the beautiful illustrations and icons, designed the word mark, and selected the typography and color palette. I designed the UX and coded everything. The final UI and layout was a true team effort.</p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>Javascript</category>
<category>React</category>
</item>
<item>
<title><![CDATA[Transpose]]></title>
<link>https://zachsteiner.com/portfolio/2015-08-20-transpose</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2015-08-20-transpose</guid>
<pubDate>Thu, 20 Aug 2015 00:00:00 GMT</pubDate>
<description><![CDATA[Web application to make note transposing easier for musicians.]]></description>
<content:encoded><![CDATA[<p><strong>Update 2024</strong>: I've since built a full functional version of this app supporting single notes, scales, and chords. The music notation layout is assisted via <a href="%5Babcjs%5D(https://www.abcjs.net)">abc.js</a>.</p>
<p><img src="/images/portfolio/transpose/cheatsheet.png" alt="Cheat Sheet"></p>
<p><img src="/images/portfolio/transpose/transpose.png" alt="Main Screen on Phone">
<em>Main Screen on Phone</em></p>
<p>I've played saxophone for more than 20 years; never professionally, but occasionally with other instruments. The saxophone family is weird and wonderful, by design. There are saxophones in all shapes and sizes, but they are unified by a common key layout. So I only need to adjust my breath support and embouchure to switch from soprano to baritone sax. A G has the same fingering on all saxophones. There's an interesting catch, however. That G that I'm playing on a soprano is not the same actual note is on an alto or baritone. Not just a different octave, but actually a different note. Soprano and tenor are B flat instruments (keeping company with trumpet, clarinet), whereas alto and baritone are E flat instruments. Thus the fun of playing transposing instruments. Due to the vagaries of transposing instruments, I've experienced the awkward situation of everyone in the band playing "Autumn Leaves" in a different key. That happened when I sat in with a jazz trio. Referencing transpositions quickly is important when you're in the heat of playing or in dark basements or clubs.</p>
<p>For when I played out, I made cheat sheets on 4'x6' index cards for alto and soprano. I've long kicked around the idea of an app to make this easier. Thus, the concept for transpose. There are a lot of transposing tools out there that take a note or scale and transpose it to another key. They have uninspiring interfaces, but more importantly they never approach the UI from a transposing instrument standpoint. I want the app to clearly answer the question: What does this particular note sound like on my instrument. If I play a G on alto sax what is that on piano? This concept shows the workflow and interface design.</p>
<p>The goal for the UI is to be quick to use and easy to read. With the device on the music stand, the result should be easy to read when glancing down. The parameters should be easy to change on the fly. The UI is simply instrument selection and a circle of fifths, which is immediately familiar to musicians. Tap the starting note on the circle and it highlights in blue. The transposed note is indicated by the triangle.</p>
<p><img src="/images/portfolio/transpose/transpose-tablet.png" alt="Tablet Version">
<em>Tablet Version</em></p>
<p><img src="/images/portfolio/transpose/transpose-selection.png" alt="Selection">
<em>Select a note.</em></p>
<p>The result is given in plain English below the circle. The app assumes some musical knowledge, but shies away from using jargon in UI copy. Though I play instruments and read music, I've never felt comfortable with music jargon. My intent is an approachable, quick reference tool. Jargon simply adds unnecessary cognitive load.</p>
<p><img src="/images/portfolio/transpose/transpose-instruments.png" alt="Select Instruments">
<em>Select Instruments</em></p>
<p>To change the instruments, tap on each to bring up the selection grid. The instruments are laid out with easy to tap targets (90px). This is more visual than a select list and requires less precision. Users potentially have a saxophone or guitar on their lap or around their neck.</p>
<p>Additional instruments can be added by making the grid scrollable. Each instrument will eventually have a custom illustration by <a href="http://jackieglimp.com">Jackie Glimp</a>. Currently, only piano and alto sax (via free vector icons) are provided as an example. The key of each instrument is used as a placeholder for the mockup.</p>
<p>I started with the idea for transpose as a simple tool to transpose single notes, but it could easily serve up chords and scales as well. In switching to chord or scale mode, the user then needs to select the target note (B-flat) and type of scale or chord. These will be select lists of the notes (in circle of fifths order) and type of chord (e.g., major triad, minor 7, etc.) or scale (minor, pentatonic, blues, various modes, etc.). Then the application presents the two chords or scales next to each other.</p>
<p>This UI would allow the user to quickly hop between keys, chord type, scale modes, and instruments. This would be particularly useful when trying to play a song in a mutually easy key for the instruments present. The app will remember the instrument selections and original note when switching between note, chord, and scale mode.</p>
<p><img src="/images/portfolio/transpose/transpose-chord.png" alt="Tablet UI">
<em>And Chords</em></p>
<p><img src="/images/portfolio/transpose/transpose-scale.png" alt="Tablet UI">
<em>Scales Too</em></p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>Design</category>
<category>JavaScript</category>
</item>
<item>
<title><![CDATA[Road Trip]]></title>
<link>https://zachsteiner.com/portfolio/2015-08-15-roadtrip</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2015-08-15-roadtrip</guid>
<pubDate>Sat, 15 Aug 2015 00:00:00 GMT</pubDate>
<description><![CDATA[Interactive UI concept to visualize stops on a road trip.]]></description>
<content:encoded><![CDATA[<p>This concept came about as a visualization for a recent road trip my wife
and I took through Western Colorado. After the trip, my wife spread a giant
state map across the kitchen table to show her parents all the places we
visited and trace our route. It gave me the idea to do something similar as
a web visualization, a cross between a map and scrapbook. This tool can
share our trip with friends and family and help us to relive the trip, the
stops cuing all of the great memories.</p>
<p><img src="/images/portfolio/roadtrip.png" alt="Training"></p>
<p>The list of locations is seeded with an external custom
<a href="http://geojson.org">GEOJSON</a> file, so additional files can be
loaded in for future trips. I did a sample Chicago and Camping Trip, as
examples. Developing this further, I would like to add more information
about each of the stops and of course more pictures. I'm still figuring out
the Mapbox API (I first started with Google Maps API and found Mapbox more
capable for what I was hoping to achieve), so the functionality will evolve
along with my knowledge of the API. I wanted to get the basic layout and
interaction fleshed out first. Actually tracing the driving route between
the points is a big feature for the future.</p>
<p>To further explore the concept, I will also demonstrate a workflow to add
locations to a trip. This is likely where geocoding will come in. I tried
using the Mapbox geocoder, but found it wanting. The GEOJSON file uses
latitude and longitude for each location currently. A good geocoding
experience would allow the user to search for an address and then approve
the location (or disapprove as with the location in Kansas I was given for
our Colorado trip). If a location cannot be found (as for certain hikes we
did), either a pin can be dropped or latitude and longitude can be pasted
from <a href="http://www.latlong.net">another tool</a>. There is also a lot
of opportunity to bring in illustration and customization to the design. It
would be great to have various user selectable trip themes (e.g., shore, US
West, major US cities, European countries, etc.) each with their own
illustrated SVG banner to reflect the location. By using SVG, the banners
could also incorporate animation or interactivity. Alternately, user
photographs could be added to the header for personalization. Currently,
there are three variants of the header: mountains, city, and camping.</p>
<p>I used the following tools to build this concept: the heavy lifting is
client-side JavaScript (no framework, database, or error handling) using the
<a href="https://www.mapbox.com/mapbox.js/api/">Mapbox JS API</a>, icons
and banner imagery are based on <a href="http://www.smashingmagazine.com/2015/07/freebie-smashicons-icon-set/">Smashicons</a>, and photos are either originals or borrowed kind folks on the internet (if this ever goes commercial, it will be populated with user submitted photos). All HTML, JavaScript, and CSS were created for this project.</p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>Web Development</category>
<category>Design</category>
</item>
<item>
<title><![CDATA[Merge]]></title>
<link>https://zachsteiner.com/portfolio/2015-08-10-merge</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2015-08-10-merge</guid>
<pubDate>Mon, 10 Aug 2015 00:00:00 GMT</pubDate>
<description><![CDATA[Interactive UI concept to for guiding the time consuming, confusing process of merging database record duplicates.]]></description>
<content:encoded><![CDATA[<p><img src="/images/portfolio/merge/merge4.png" alt="Merge"></p>
<h2>The Problem</h2>
<p>Administrators of data systems often have to import data files or accept data from third-party systems. Import cuts down manual data entry, but data is rarely clean or current. This results in conflicts in records. Resolving these conflicts is typically a tedious process of finding and manually fixing conflicts. However, algorithmically merging has considerable risks involved. Typically, resolving conflicts requires a person's approval to ensure data integrity. It's also a relatively risky task because most databases join records in a destructive manner. Once the submit button is pressed the records are forever together.</p>
<h2>Intended User</h2>
<p>HR staff that administer a large corporation's employee management system.</p>
<h2>This Concept</h2>
<p>This interface concept demonstrates a method for merging duplicate records when importing data from a file or another data system. In this example, a user imported an Excel file of employee information. There are multiple suspected duplicate employees. Duplicates without differences can be merged at the click of a button or en masse. Those with differences—maybe "Bob" versus "Robert" or a transposed number in a employee number—will require walking the user through a guided merge process. The result is a process that helps the user feel comfortable the resulting data is clean by providing feedback and transparency through the entire process. Despite being a manual process, the interface gives a clear path and empowers the user to quickly make informed decisions. Typical merge operations are destructive, but this method (through a hypothetical, probably NoSQL database) creates a new version of the employee record linked to the previous two, so that merging/separating can be non-destructive. Thus, a user can abandon or undo work as needed through the workflow.</p>
<p><em>Note:</em> The interactive prototype is intended to demonstrate interactions. This demo does not save information between pages, either client-side or in a database. It is purely to provide an overview of a interface solution to common data system workflow. Refreshing pages or using the back button will not remember state as a real application would</p>
<h2>Executive Summary (tl;dr version)</h2>
<ul>
<li>Users sees a list of suspected duplicate records with percent match. User can merge 100% matches and resolve conflicts.</li>
<li>Users sees a quick overview of conflicts between the records they are merging.</li>
<li>Users resolve conflicts in a guided process by using mouse, touch, or keyboard.</li>
<li>The final merged record is shown before saving it. The merge can be undone immediately or at a later date.</li>
</ul>
<p>Read the <a href="https://merge.zachsteiner.com">full description</a></p>
<h2>Technical Information</h2>
<p>This concept was built with HTML, CSS3, JavaScript/jQuery, and SVG. Works in modern browsers that support <a href="http://caniuse.com/#feat=svg">SVG</a> and <a href="http://caniuse.com/#feat=flexbox">Flexbox</a>. Browser support aided by <a href="https://github.com/postcss/autoprefixer">Autoprefixer</a>.</p>
<p>Grid system is <a href="http://neat.bourbon.io">Bourbon Neat</a>. The demo is responsive, so phones are welcome.</p>
<p>Typeface is <a href="http://weiweihuanghuang.github.io/Work-Sans/">Work Sans</a>. List filtering is provided by <a href="http://www.listjs.com">List.js</a>. Icons are from <a href="https://github.com/google/material-design-icons">Material Design Icon Set</a>.</p>
<p>Source Code is available on <a href="https://github.com/zsteiner/merge">Github</a>.</p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>Design</category>
<category>Javascript</category>
</item>
<item>
<title><![CDATA[Intake]]></title>
<link>https://zachsteiner.com/portfolio/2015-08-05-intake</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2015-08-05-intake</guid>
<pubDate>Wed, 05 Aug 2015 00:00:00 GMT</pubDate>
<description><![CDATA[Design concept for streamlining intake process for medical offices.]]></description>
<content:encoded><![CDATA[<p>Anyone who has been to a doctor, dentist, counselor, or other healthcare provider knows and has been at least mildly annoyed by intake forms. The overwhelming, at times confusing, stack of paperwork that greets us at our first appointment. To the right are examples from a dentist and a counseling practice. Both came up in a Google image search. They are provided as typical examples, not to specifically pick on their user experience.</p>
<p><img src="/images/portfolio/intake/example-form2.png" alt="Dentist Office Intake Form">
<img src="/images/portfolio/intake/example-form.png" alt="Counseling Practice Intake Form"></p>
<p>Typically, intake forms are handed to a patient on a clipboard to fill before the visit. An administrative assistant or other staff then key enters the handwritten form into whatever Electronic Medical Record or data system the office uses. The paper form is sometimes consulted by the nurse/doctor/counselor during the first visit, but is generally is filed for archival purposes after data entry.</p>
<p>Paper forms generally have poor user experience (poorly written instructions, unclear copy, inadequate space for answers) and they offer no real time validation. They necessitate additional labor of data entry for each new patient/client. Further, there's room for error in the data entry person's interpretation of hasty hand written responses. Simply digitizing existing forms would allow validation and obviate data entry, but would many of the usability issues would carry over. Design constraints of paper forms (physical space, legibility, human handwriting) just don't apply on a screen (no matter how much we try to <a href="http://kumailht.com/gridforms/">replicate it</a>).</p>
<p>This interface concept seeks to move intake forms from an error-prone, time consuming task to a more conversational, friendly experience that can augment service and care. Click any image for a larger view of the mockup.</p>
<h2>Beyond the clipboard</h2>
<p>In place of the traditional clipboard, intake can be completed on a provided tablet (iPad in <a href="https://support.apple.com/en-us/HT202612">Guided Access</a> mode would be great here). Alternately, the patient/client can be sent a single serve link to their smart phone and do the paperwork either at home or in the waiting room. A tablet or smart phone provides a great opportunity for an optimized touch interface. Rather than having to write/type every entry, there are a lot of opportunities for easy to tap buttons tailored for the question at hand.</p>
<p>Rather than using the paper form as a metaphor, this concept takes the clinical interview as the metaphor. The interview begins with basic information to build rapport before building on previous responses to solicit more sensitive information (e.g., medical history, drug use, mental health issues, sexual behavior). As with a conversation, there's a flow from question to question. In this case, questions are generally answered one at a time.</p>
<p>Adopting a conversational style can help the client understand the questions better and provide more complete/truthful answers. Contrasted with more stilted terms like "Responsible Party" or "Current Occupational Status". The copy on screen should be targeted at an eighth grade reading level to be accessible to a range of clients/patients (this is the guideline for broadly accessible surveys). The tone strives to replicate the friendly, though professional, tone of a good clinician.</p>
<p>The guided nature allows the application to only present relevant information to speed completion. For instance, if the patient says he is male, then it can skip all pregnancy related questions. Similarly, information about parent/guardian would only be presented, if the client/patient is younger than 18.</p>
<p>This touch frontend would need to sync with the data system or EMR of choice (easier said than done, though EMR interoperability is slowly becoming a reality). This is critical to remove the additional time and chance for error from manual key entry.</p>
<p>Finally, the data can automatically be formatted for PDF or print at the end. This will build trust with office workers and service providers that are comfortable with paper forms. Further, regulations around archiving can be satisfied without additional staff time. The formatted version can surpass the standard paper-only version by organizing and highlighting information contextually. For example, a family history of heart disease and high cholesterol can be displayed more prominently than just circles on a list.</p>
<h2>Guide the conversation</h2>
<p>The intake conversation begins with basics like age and gender. The system should already know the patient/client's name from the office scheduling the appointment and initiating the intake process. Already, one less piece of information to enter.</p>
<p><img src="/images/portfolio/intake/gender-tablet.png" alt="Gender Question on tablet">
<img src="/images/portfolio/intake/gender-phone.png" alt="Gender Question on phone"></p>
<p>For gender, the application can be more inclusive (gender rather than sex, inclusion of transgender) than the typical paper form and icons help make the more complex concept of gender (as opposed to biological sex) more accessible to a broad audience.</p>
<p><img src="/images/portfolio/intake/birthday-tablet.png" alt="Date of Birth Question on iPad"></p>
<p>Date of birth should be positioned near the beginning of the process because it can be used to inform future questions. For instance, if the age is under 18, the system can prompt questions about parent or guardian. Various age-based milestones can be built into the question logic as well. For instance, patients near 50 can be asked about colonoscopy or teen clients for a counselor can be asked age specific mental health risk screening questions (e.g., around bullying). Age does not need to be entered separately as it can be easily calculated from date of birth, removing another redundant field from standard forms.</p>
<p>Datepickers are often awkward on touch devices. After years of using spinners on iOS, I still haven't really gotten the hang of them. Though I would not advocate replacing native UI components in all cases, this is a case where the data entry is straight forward and can be expedited with custom input buttons. Each month is easy to tap. The day and year can be quickly tapped out with the 10 key pad on screen (this could be replaced by the native number keypad on phone, which is absent on iPad). "19.." and "20.." provide a shortcut for entering year of birth.</p>
<p><img src="/images/portfolio/intake/insurance-tablet.png" alt="Insurance Question on iPad"></p>
<p>For healthcare providers in the US, patient/client insurance is a key piece of information. The options are big, easy to tap buttons, rather than a checkbox or radio button (which are much too <a href="http://www.smashingmagazine.com/2012/02/21/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/">small a touch target</a>, no matter which <a href="https://msdn.microsoft.com/en-us/library/windows/apps/hh465326.aspx">guidelines</a> <a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/LayoutandAppearance.html">you</a> <a href="http://developer.android.com/design/patterns/accessibility.html">follow</a>).</p>
<p>Insurance can be used for branching as well. Those with insurance can be directed to insurance information. Those without can be directed to payment information. Tools available in consumer apps (particularly those for payment processing) can be leveraged for entering insurance information more easily and accurately. First, patients could select from a list of approved insurers for the provider (whether a provider accepts a patient's insurance is a common point of confusion). Group number and ID numbers could be validated for length and format for the selected insurer; much as a Visa card is validated form proper format, not necessarily the correct number. The device camera could scan the insurance card as many apps read credit cards currently. Use of the camera would obviate data entry and requisite office photocopies.</p>
<h2>Collect sensitive information</h2>
<p><img src="/images/portfolio/intake/mentalhealth-tablet.png" alt="Feelings Question on iPad"></p>
<p>A richer intake experience can help solicit information from counseling or social service clients that would be difficult for a paper form. Borrowing cues from tagging interfaces, clients can tap their recent feelings or behaviors. A similar method can be applied to mental health related behaviors, medical history, or (sexual or drug-related) risk behaviors. There are possibilities for ranking of symptoms or search/filter lists for long lists of symptoms, conditions, or prescription drugs. A patient could search for a drug allergy and avoid misspellings (I still have to look up the spelling of erythromycin after that bad experience as child). Patients on a larger number of prescription drugs could have the space they need to add all of their medications, rather being limited to space on a page.</p>
<p><img src="/images/portfolio/intake/rating-tablet.png" alt="Anxiety Rating"></p>
<p>Based on the selected items on the previous screen, the patient can be contextually delivered follow-up questions. In a mental health context, this could be severity ratings of feelings or symptoms (as a <a href="http://ajbr.org/Archives/Using%20Fine-Grained%20Likert%20Scales%20in%20Web%20Surveys.pdf">fine-grained Likert scale</a> potentially). For current prescriptions, it could be information on dosage and length taking the drug. For family medical history questions, it could ask specifics of which relatives had heart disease or high cholesterol.</p>
<p>Collecting all of this rich data has the potential enhance service. The counselor or doctor can readily have access to a summary of the client/patient's answers. The report can prioritize particular feelings or behaviors (e.g., suicidal ideation) or highlight high-risk combinations of responses (e.g., depression and drug use) to aid in clinical decision making. Having a clearer idea of what the patient/client reported can help build rapport during the first interaction.</p>
<h2>Taking it a step farther</h2>
<p>An article on <a href="https://medium.com/@jahedmomand/slack-for-healthcare-what-enterprise-collaboration-and-messaging-can-do-for-the-future-of-f6ec82df84a0">implementing Slack for Healthcare</a> helped crystalize my thinking on this interaction concept. If the metaphor for this interaction is a clinical interview, why not making it more like conversation? Rather than static screens, a Slack Bot-like AI would ask and respond to questions for the intake process. Where appropriate the patient/client could type responses or the UI could contextually serve up appropriate response buttons (Yes/No buttons or Male/Female/Transgender buttons) for one tap responses.</p>
<p>A challenge with this method is that would be more difficult to collect health history or mental health risk behaviors. Also, it would be difficult to show progress toward completion. Paper is finite, so there's always a cue as to how close the patient is to completion. Progress indicators can mitigate this for the above concept. The patient could see a countdown of remaining items to answer as a way of cueing progress toward their goal.</p>
<p>Finally, both the above concept and a hypothetical messaging-based version would need to clearly show a final version of the patient's information for review before finalizing. By answering each piece of information in turn, it could be easy to lose the forest for the trees. A clearly formatted version of the patient's response would offer good feedback on their work. The patient would also need the ability to quickly edit any information from this final view.</p>
<p>All mockups were created in
<a href="https://www.sketch.com">Sketch</a>. Icons are from
<a href="http://icomoon.io">icomoon.io</a>.</p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>Design</category>
</item>
<item>
<title><![CDATA[Campaign Wordcounts]]></title>
<link>https://zachsteiner.com/portfolio/2015-07-31-wordcounts</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2015-07-31-wordcounts</guid>
<pubDate>Fri, 31 Jul 2015 00:00:00 GMT</pubDate>
<description><![CDATA[Interactive data visualization based on presidential platforms.]]></description>
<content:encoded><![CDATA[<p><img src="/images/portfolio/wordcounts.png" alt="Word counts"></p>
<p>Back in 2008, while working on a conference submission (and thus hypersensitive to word counts), I wrote a <a href="/posts/2008-09-05-presidential-word-counts/">blog post</a> about word counts on campaign policy position statements. I noticed there was a huge range of ink spilled on various issues between the two candidates at the time. I intended to do a follow up in 2012, but I was otherwise occupied during the election (dissertation, graduating, moving). However, 2016 is proving to be an interesting election cycle. There is a huge field of very diverse candidates ranging from experienced politicians to relative unknowns. I decided to expand the concept as an interactive data visualization. As a comparison, 2008 McCain and Obama and 2012 Romney (actually late 2011, for fairer comparison to the 2016 crop) are included as reference. Immediately noticeable is the fact that many candidates have a lot written already on issues, whereas eight have no policy/issue offerings on their website. This does not seem to be a partisan divide; there are verbose and laconic candidates from both parties. Granted this is still early in the election cycle and many of the campaigns are still nascent. I suspect policy positions will become more substantive for the two remaining candidates after the primaries, as they were for McCain, Obama, and Romney. I will update the underlying data as candidates join, leave, or update their sites.</p>
<p>My methodology is simple: I copied and pasted the full text of pages or PDFs into a word processor and recorded the word count. For those candidates that offer a summary in addition to a full policy policy paper (typically as a PDF or eBook download), I use the full paper text (for example Romney's 39,987 word economic tome). The number of issues for each candidate reflects those presented on the candidate's site, rather than the count of my categories. My categories collapse specific issues for clearer cross-candidate comparisons. For instance, I include tax and job issues under the umbrella of Economy; defense spending and Iran policy under Foreign Policy. A lot of disparate issues get lumped into Social Issues—including criminal justice, 2nd Amendment, voting rights, income inequality, marriage equality, religious liberty, etc. I tried to keep the same issue categories from 2008 for better comparison. I did remove Iraq (not an issue written about in 2015) and added Environment and Social Issues (discussed now more than in 2008). Also, I noticed that every candidate, whether they had policy information or not, devoted a section of their site to biography, so I included word counts for those as well. New categories are marked with a *</p>
<h2>The Issues</h2>
<ul>
<li>Economy</li>
<li>Education</li>
<li>Energy</li>
<li>Foreign Policy</li>
<li>Health Care</li>
<li>Technology</li>
<li>Social Issues *</li>
<li>Environment *</li>
<li>Biography *</li>
</ul>
<p>Average word counts are for my reported categories. For all of these, the absolute values of numbers is less interesting than the comparisons between candidates. None of this is intended as an endorsement or rejection of the actual content of the policy positions. There are can be better articulated 1,000 word positions than 10,000 word positions, though I do find the 100 to 20,000 word difference interesting. This visualization is an examination of the messaging strategies candidates employ, rather than a critique of candidates' platforms. I'm putting a lens to the depth and breadth that candidates choose for various important issues.</p>
<p>You will see all of the current candidates on the left sidebar (or in the mobile drawer). You can toggle as many candidates for comparisons, though I'd recommend 6-8 for easy visual comparison. The buttons below the chart allow you to switch between word counts per issue, total issues covered, and average words overall. All three retain the selected candidates. Below the graph, you can see a summary of the candidate and clicking "learn more" takes you to their official site.
My raw data can be examined for accuracy: <a href="data/issues.json">Issues</a>, <a href="data/count.json">Issue Counts</a>, and <a href="data/average.json">Average Word Counts</a>. All are in JSON format.</p>
<h2>Notes</h2>
<p>Lawrence Lessing is a referendum, or single issue, candidate. He is running to fix campaign finance reform, after which he pledges to resign. I included this under the Social Issues for lack of a better category.
Romney 2012 had two massive policy papers outlining economic and foreign policies, thus the much larger word counts. Economic policy was divided into 7 individual issues. I broke out Energy.</p>
<p>Clinton covers Policy in “Four Fights” section. Economy is “Building an economy for tomorrow”. Foreign Policy is covered in “Defending America and our core values.”</p>
<p>Christie has four PDF policy papers. “Entitlement Reform” is mainly about Medicare and Medicaid so I included it in Health Care, but it could also be included under Economy. Christie's about section is a video with photo collage with captions.</p>
<p>Marco Rubio’s larger word count for Foreign Policy comes from a series of op-ed articles he wrote ranging from “Cuba”, “Iran”, “Iran, Part 2”, “Europe”, “Israel”, and “Defense Spending”. These were previously published in various magazines and websites.</p>
<p>Santorum has not officially released his economic plan as of the end of July, but the site teases that is coming soon. I will update when it is published.</p>
<p>For Jim Webb, I combined “Economic Fairness” and “National Infrastructure” for economy.
I updated the biography word counts for 2008 McCain and Obama from <a href="http://archive.org">archive.org</a> cached versions of their sites.</p>
<h2>Technical Information</h2>
<p>All data is from candidate websites as of the date updated. This visualization was built with JavaScript and HTML/CSS3. Charts are generated from data files using <a href="http://c3js.org">c3.js</a>, which is based on <a href="http://d3js.org">d3.js</a> Scrolling is provided by <a href="http://iscrolljs.com">iScroll</a>. The source can be found on <a href="https://github.com/zsteiner/wordcounts">Github</a>.</p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>Web Development</category>
<category>Design</category>
</item>
<item>
<title><![CDATA[UX Principles]]></title>
<link>https://zachsteiner.com/portfolio/2015-07-13-ux-principles-portfolio</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2015-07-13-ux-principles-portfolio</guid>
<pubDate>Mon, 13 Jul 2015 00:00:00 GMT</pubDate>
<description><![CDATA[Guiding principles for user experience and product design.]]></description>
<content:encoded><![CDATA[<p>I've maintained a living list of guiding principles for my UX work. The principles are a synthesis of established UX thought, applications of psychological research, and personal experience. Rather than keeping it in my head and on various white boards, I've formalized it as post on zachsteiner.com.</p>
<p>Below is a living list of principles that guide my user experience work. I've maintained this list offline for sometime, but now seems a good time to share. These principles are not exhaustive by any means. Nor are they completely original. Some of the ideas are informed by my graudate coursework and research. Many are adapted from more experienced UX thinkers. I owe a lot of this list to Jakob Nielson's <a href="http://www.nngroup.com/articles/ten-usability-heuristics/">10 Usability Heuristics</a>, which motivated me to start the list. It's also surprising how little has changed in 20 years. Many of the other principles then coalesced through reading myriad articles/article, interactions with users, conversations with colleagues, and the odd memory from grad coursework.</p>
<h3>Software is for humans...</h3>
<p><em>... and not the other way around</em>. Software is a tool created to help people solve some problem. A piece of software should be designed to solve said problem. A person should not have to redesign their behavior to suit the needs of software. This is manifest in small ways like requiring redundant data entry on a checkout form to major ways as when a convenient database design dictates an unnatural workflow. The line "What is easy for the developer, is rarely easy for the user" gets tossed around, but there is a lot of truth to it and speaks to the challenges of user-centered design. The hard work to discover user needs and build a solution to address their needs is well worth it. The even harder work of accepting feedback then iterating, refining, and never resting on our laurels is what truly helps us create person-centered software.</p>
<h3>Be "easy to learn", not "intuitive"</h3>
<p>"Intuitive" is tossed around a lot in reference to software. <a href="https://www.google.com/#q=intuitive+ux">Lots of articles</a> will tell you how to add some intuitive pixie dust to your software and some even offer legitimately useful strategies for interface design. The problem is that software is not <strong>inherently intuitive</strong>. Many of us have built a rich experience (good and bad) with using software, however. Rather than assuming the user will know what to do intuitively, designers need to align with user expectations and help the user along in places where the software needs to be novel. Integrated, contextual <a href="http://www.nngroup.com/articles/stagnating-expertise/">just-in-time learning</a> is a great alternative to manuals or training videos for those truly new or difficult interactions. It can be difficult to unlock the full power of complex software, but software need not be impenetrable. A properly designed experience can get a user doing the basics quickly while offering opportunities for discovery of more complex functionality. Well designed information architecture, clear UI copy, sensible defaults, and useful feedback can all contribute to this ease of learning. Logic Pro and Sketch both do this rather well considering the power/complexity of the software, especially compared to some statistics and design software that will go unnamed.</p>
<h3>Build trust</h3>
<p>I previously researched trust in supervisor and organizations, but this concept seems underappreciated in UX. Building user trust should be a key a goal of UX design. User trust has big implications for user retention, purchasing behavior, and other metrics business care about. Thus, there is a great business case for building/maintaining trust in software. Though, trust is really easy to violate and notoriously difficult to rebuild. Violating user expectations or impairing user work can damage trust. Deleting a lot of work is a major violation, but small design choices can add up over time to violate trust (death by a thousand cuts). Interpersonal apology literature has applicable suggestions for strategies to rebuild trust (<a href="http://dx.doi.org/10.1037/a0019993">Fehr & Gelfand, 2010</a>) when something goes wrong:</p>
<ol>
<li>Offer corrective action. That is, what will software do to make the situation right (this dovetails with offering forgiveness)</li>
<li>Demonstrating empathy. That is, show understanding of the situation and how the user may feel at the moment.</li>
</ol>
<h3>Provide feedback</h3>
<p>The core of building trust with any software is providing feedback. Software often completes tasks without letting the user know what just happened or provides confusing/vague/incomplete information. When a user saves her work, the software should provide that feedback. Beyond the basics, feedback is a great place to demonstrate empathy. Slack's <a href="https://medium.com/@awilkinson/slack-s-2-8-billion-dollar-secret-sauce-5c5ec7117908">irreverent tone</a> is part of its success, but irreverence and emoji would likely come off as grating or tone deaf when used to report a system outage or deleting important files/records. A thorough understanding of the work to be done and the needs of users provides the basics what is appropriate in regard to tone of feedback. Some situations call for a sassy friend and some call for the friendly, but professional tone of a good doctor.</p>
<h3>Be transparent</h3>
<p>This principle came from my observation of how many data systems operate. Often a user will enter some data and then it is sent into an apparent blackbox after hitting submit. Many systems forgo the basic reassurance that the data was actually saved. To further deepen the black box, finding previously entered data is often difficult to do. This makes it really hard for the user to trust her software. This is why many of the users I worked with could never conceive of going paperless; hard copies afforded them a sense of security and trust the software denied them. Beyond the frustration of iTunes changing user's carefully organized music libraries (and adding DRM), <a href="http://www.theverge.com/2015/7/1/8877129/apple-music-icloud-problems">much of the frustration toward Apple's iTunes Match</a> can be attributed to the lack of transparency. iTunes identifies music "automagically" (though often incorrectly) and then there is no transparency or offering of forgiveness (the next principle) once it is done. Whether we are dealing with a user's financial data or her carefully organized media collection, software should be upfront with what it is doing. If something goes wrong, we have the next principle.</p>
<h3>Offer forgiveness</h3>
<p>Another principle borrowed from <a href="http://www.nngroup.com/articles/stagnating-expertise/">Nielson</a>. So often software does a task and there is no going back. This impairs trust and fosters a culture of fear of software. Users become afraid to explore software because they don't want to "break something". People make mistakes and software should be sensitive to that. No button (or combination of buttons) should ever "break" a system. If a task truly offers no return, the software should adequately warn the user and allow an escape hatch before the critical moment. Don't be the software that allows a student to delete his term paper at 4am the day it is due.</p>
<h3>Only be clever enough...</h3>
<p><em>... but not too clever</em>. Designers can become enamored with the novelty of new UI pattern or method of presenting information. At best, a user will likely not notice or care about the novelty of the UI. At worst, the task at hand may be obfuscated by the design choice (see any scrolling site that requires scroll down label or button). Early on, I often designed fussy UIs for the sake of trying something different. Through design iterations, many became drastically simpler and clearer to the user. This relates back to the first principle.</p>
<h3>Conserve precious cognitive resources</h3>
<p>I took a grad seminar in training co-taught by a training expert and a cognitive psychologist that has stuck with me since. A major theme of the seminar was the notion of cognitive load. The human perceptual system and cognition are much more limited in bandwidth than we typically think. This is generally talked about at its most basic level in regard to working memory with the old chestnut: we can retain 7 items ± 2. This is not a hard and fast rule, let alone a law, and really only applies for arbitrary items (like 7 random digits or letters). The spirit of this chestnut, though, holds. Interfaces need to conserve the users limited resources of attention, memory, and cognitive processing. Anxiety and fear take up precious resources that can be used more productively. <a href="http://thomasbyttebier.be/blog/the-best-icon-is-a-text-label">Vague icons</a>, byzantine navigation, unexpected/confusing interactions, lack of consistency, and many more interface blunders can increase the cognitive load of using software. Remember the user has a task she needs to accomplish.</p>
<h3>Don't induce nausea (or headaches)</h3>
<p>This is a somewhat flippant phrasing for the important concept of accessibility. Some months ago I encountered an animated data visualization that literally made me dizzy and a bit queasy. I know a few people that feel dizzy viewing parallax animations. Apple ran into this issue for a subset of users with iOS 7 and had to compensate via user preferences. The goal is to help your users to feel comfortable while using software and being sensitive to not excluding a set of users. This covers everything from sensitivity to those with a tendency toward vertigo to designing for the visually impaired.</p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
</item>
<item>
<title><![CDATA[Luther Consulting Help System]]></title>
<link>https://zachsteiner.com/portfolio/2015-03-01-luther-help</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2015-03-01-luther-help</guid>
<pubDate>Sun, 01 Mar 2015 00:00:00 GMT</pubDate>
<description><![CDATA[Redesign of help system with goals of providing clearer organization and more information about help resources.]]></description>
<content:encoded><![CDATA[<p><img src="../../images/portfolio/help/help-before.png" alt="Help Page Before"></p>
<p><img src="../../images/portfolio/help/schemas-before.png" alt="XML Schema Before"></p>
<p>After a conversation with a co-worker expressing frustration with the company's <a href="https://help.lutherconsulting.com">help resources</a>, I mounted a redesign. User-facing help resources were split between two different HTML pages. Each had begun when the number of resources (and staff to support them) was much smaller. Thus, there wasn't consideration of information architecture or layout because there were only a few links and files for each. As the number of resources grew, they had more difficulty accommodating them. Finally, the types of content expanded from a couple of user guides to quick references, computer-based trainings, videos, and data entry templates, documents.</p>
<p>The goals of the redesign were to:</p>
<ul>
<li>Clarify organization and information architecture</li>
<li>Provide more information about each resource</li>
<li>Provide information about how to seek help from technical support</li>
<li>Present a cohesive experience across the resources</li>
</ul>
<p><img src="../../images/portfolio/help/help-after.png" alt="New Help Landing">
<em>New Help Landing</em></p>
<p><img src="../../images/portfolio/help/help-training.png" alt="Training Resources After">
<em>Training Resources After</em></p>
<p>To address the first goal, I worked with internal stakeholders to identify the main categories of content and how to best organize the content within them. We arrived at Training, Templates, Upload, Resources (the grab bag). The landing page provides a jumping-off place for each and a description of the category. The navigation makes it very easy to jump from category to category.</p>
<p>Within the page, we had the decision to organize by content category or type of resource (e.g., user guide, video, etc.). Despite previously organizing by type, organizing by content fits more with how users will engage with the content. The user doesn't think "I want to watch a video today", but rather "How do I enter my budget information?". If the help resources offer a quick reference and a video, the user can then choose how much information they want/need because they are both presented together. I organized the resources by brevity: quick references first, then user guides, then videos, and finally computer-based trainings.</p>
<p>To fulfill goal 2, I worked with the training team to add blurbs about each resource, so users can have some more information beyond the title as to what they will find. Additionally, I added the type of resource and time to complete (for videos and computer-based training). Finally, I added the updated date so the user can know how recent the resource is.</p>
<p><img src="../../images/portfolio/help/help-support.png" alt="Support"></p>
<p>Though the technical support email and phone number were prominently displayed across company resources and trainings, there were some gaps in information on how to contact technical support. For instance, hours for phone support were missing. Going a step further, I added time detection JavaScript to cue whether it was likely the company was open. This is important for a company that is confusingly positioned in Eastern Time but services customers nationwide. This little client-side script proved an interesting challenge. I used <a href="http://momentjs.com">Moment.js</a> to detect the time and <a href="http://momentjs.com/timezone/">Moment Timezone</a> to detect the user agent time. Then the script had to detect the day and convert the time into Eastern time. The UI copy is somewhat of a hedge by saying "We're likely open right now". There's no good way to detect holidays. However, the copy does make a point to say that technical support is open on Federal holidays, an important consideration when working with government employees.</p>
<p>Finally, I added some frequent issues and key partner contact information to provide an additional reference for information technical support staff have to give out via email or on the phone regularly.</p>
<p><img src="../../images/portfolio/help/help-schemas.png" alt="Upload Resources"></p>
<p>The final goal was to provide a cohesive experience across the resources. The before screenshots had resources split between two pages with very different formats. The redesign brought all of the resources into a predictable format. The upload information benefitted even more than training materials. As upload information is versioned, it became important to differentiate the current version from archived versions (that are still used by certain users). All of the sections have keyword filtering courtesy of <a href="http://listjs.com">list.js</a> to make, particularly the history, easier to digest.</p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>User Research</category>
<category>Information Architecture</category>
<category>Web Development</category>
<category>Design</category>
</item>
<item>
<title><![CDATA[Film Covers]]></title>
<link>https://zachsteiner.com/portfolio/2014-10-01-film-covers</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2014-10-01-film-covers</guid>
<pubDate>Wed, 01 Oct 2014 00:00:00 GMT</pubDate>
<description><![CDATA[Custom covers for digital film library]]></description>
<content:encoded><![CDATA[<p>When I digitized the experimental shorts of Maya Deren and Kenneth Anger for easier viewing, I needed cover art for iTunes. I created layouts for each cover. For the Maya Deren films, I incorporated typography and a still image from each film. For the Anger films, I highlighted the wonderful hand drawn title cards from the films.</p>]]></content:encoded>
<author>Zach</author>
<category>Design</category>
</item>
<item>
<title><![CDATA[Luther Consulting Site]]></title>
<link>https://zachsteiner.com/portfolio/2014-09-01-luther-site</link>
<guid isPermaLink="false">https://zachsteiner.com/portfolio/2014-09-01-luther-site</guid>
<pubDate>Mon, 01 Sep 2014 00:00:00 GMT</pubDate>
<description><![CDATA[Responsive redesign of company's marketing site.]]></description>
<content:encoded><![CDATA[<p><img src="/images/portfolio/luther/luther.png" alt="Contact"></p>
<p>I did a responsive redesign of the company's external marketing site to match the new product direction and be more accessible to mobile users.</p>
<p><img src="/images/portfolio/luther/luther-contact.png" alt="Contact Page"></p>
<p>Color schemas, icons, and typography are carried over from global styles to create a unified experience between new products and marketing site.</p>
<p>The design began as mockups in Sketch before moving into hand coded HTML. I developed content with input and direction from a variety of internal stakeholders. The design evolved through iterations over a several month period.</p>
<p>The redesign reorganized product and contact information, but required new content. I created new content pages for "about the company" and careers. The culture is very important at Luther Consulting, so the site needed to communicate it clearly to customers and job seekers. For instance, the previous site design did not feature the company's mission statement, which was a powerful recruitment tool that had initially attracted numerous employees. Previously, job postings were distributed via email or Linkedin, so there was less opportunity for the company to tell its story to job seekers.</p>
<p>This redesign provided a place to experiment with new techniques (responsive web design, SVG) that were implemented in the eventual product design. It also provided a place to iterate and evolve within the nascent visual design style.</p>
<p><img src="/images/portfolio/luther/luther-nav.png" alt="Mobile Navigation">
<img src="/images/portfolio/luther/luther-mobile.png" alt="Mobile Homepage"></p>]]></content:encoded>
<author>Zach</author>
<category>UX</category>
<category>Web Development</category>
<category>CSS</category>
<category>JavaScript</category>
</item>
</channel>
</rss>