diff --git a/src/ui/styler/ErrorFieldStyler.js b/src/ui/styler/ErrorFieldStyler.js
new file mode 100644
index 0000000..a065047
--- /dev/null
+++ b/src/ui/styler/ErrorFieldStyler.js
@@ -0,0 +1,132 @@
+/**
+ * Error condition field styler
+ *
+ * Copyright (C) 2016 LoVullo Associates, Inc.
+ *
+ * This file is part of liza.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+var Class = require( 'easejs' ).Class,
+ FieldStyler = require( './FieldStyler' );
+
+
+/**
+ * Style field to indicate an error and displays an error message
+ */
+module.exports = Class( 'ErrorFieldStyler' )
+ .extend( FieldStyler,
+{
+ 'public getId': function()
+ {
+ return 'error';
+ },
+
+
+ 'public applyStyle': function( field, element, row, msg )
+ {
+ var _self = this;
+
+ // style the row containing the element
+ for ( var i in row )
+ {
+ this.addClass( row[ i ], 'invalid' );
+ }
+
+ // TODO: legacy; remove
+ this.addClass( element, 'invalid_field' );
+
+ // display the error message
+ this._createMessage( field.getName(), msg, row[ 0 ], row[ 1 ] );
+
+ return this;
+ },
+
+
+ 'public revokeStyle': function( field, element, row )
+ {
+ var _self = this;
+
+ // un-style the row containing the element
+ // style the row containing the element
+ for ( var i in row )
+ {
+ this.removeClass( row[ i ], 'invalid' );
+ }
+
+ // TODO: legacy; remove
+ this.removeClass( element, 'invalid_field' );
+
+ this._destroyMessage( row[ 0 ], row[ 1 ] );
+
+ return this;
+ },
+
+
+ 'private _createMessage': function( name, message, dd, dt )
+ {
+ // we can only generate the message if the parent row is available
+ if ( !( dd && dt ) )
+ {
+ return;
+ }
+
+ var msg = document.createElement( 'div' );
+ msg.className = 'errmsg';
+ msg.innerHTML = message;
+
+ // append to dd
+ dd.appendChild( msg );
+
+ var height = ( msg.offsetTop + msg.offsetHeight );
+
+ // element does not have height until added to DOM
+ // set a default to ensure it appears to user
+ height = ( height === 0 )
+ ? 45 + 'px'
+ : ( height + 10 ) + 'px';
+
+ dd.style.height = height;
+ dt.style.height = height;
+ },
+
+
+ 'private _destroyMessage': function( dd, dt )
+ {
+ if ( !dd )
+ {
+ return;
+ }
+
+ dd.style.height = '';
+
+ // note that dt may not actually exist (in fact, dd may not even be a
+ // dd; we should rename these variables)
+ dt && ( dt.style.height = '' );
+
+ var node;
+
+ // search for the message node, starting with the last element (since
+ // the error message was appended, we're likely to find it on our first
+ // try)
+ for ( node = dd.lastChild;
+ node && node.className !== 'errmsg';
+ node = node.previousSibling
+ );
+
+ // if we found it, then remove it
+ node && dd.removeChild( node );
+ }
+} );
diff --git a/src/ui/styler/ErrorStyler.js b/src/ui/styler/ErrorStyler.js
new file mode 100644
index 0000000..714aaf6
--- /dev/null
+++ b/src/ui/styler/ErrorStyler.js
@@ -0,0 +1,108 @@
+/**
+ * Error condition field styler
+ *
+ * Copyright (C) 2016 LoVullo Associates, Inc.
+ *
+ * This file is part of liza.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+var Class = require( 'easejs' ).Class,
+ Styler = require( './Styler' );
+
+
+/**
+ * Handle error generation and defer styling to supertype
+ */
+module.exports = Class( 'ErrorStyler' )
+ .implement( Styler )
+ .extend(
+{
+ /**
+ * Hash of error messages by field name
+ * @type {Object}
+ */
+ 'private _msgs': {},
+
+
+ /**
+ * Initialize error styler with a hash of error messages by field name
+ *
+ * @param {Object} msgs hash of error messages by field name
+ */
+ 'virtual __construct': function( msgs )
+ {
+ this._msgs = msgs;
+ },
+
+
+ 'public getHooks': function( uistyler )
+ {
+ var _self = this;
+
+ return {
+ fieldError: function( context, failures, msgs )
+ {
+ msgs = msgs || {};
+
+ for ( var name in failures )
+ {
+ var msgset = ( msgs[ name ] || [] );
+
+ for ( var index in failures[ name ] )
+ {
+ // if no error message was provided, fall back to one of
+ // the defaults
+ var msg = (
+ msgset[ index ]
+ || _self._msgs[ name ]
+ || "Field is invalid"
+ );
+
+ _self.onFieldError(
+ context.getFieldByName( name, index ),
+ msg
+ );
+ }
+ }
+ },
+
+ fieldFixed: function( context, fixed )
+ {
+ for ( var name in fixed )
+ {
+ for ( var index in fixed[ name ] )
+ {
+ _self.onFieldFixed(
+ context.getFieldByName( name, index )
+ );
+ }
+ }
+ }
+ };
+ },
+
+
+ 'virtual protected onFieldError': function( field, msg )
+ {
+ // do nothing by default
+ },
+
+
+ 'virtual protected onFieldFixed': function( field )
+ {
+ // do nothing by default
+ }
+} );
diff --git a/src/ui/styler/FieldStyler.js b/src/ui/styler/FieldStyler.js
new file mode 100644
index 0000000..f535e30
--- /dev/null
+++ b/src/ui/styler/FieldStyler.js
@@ -0,0 +1,91 @@
+/**
+ * Style fields using CSS
+ *
+ * Copyright (C) 2016 LoVullo Associates, Inc.
+ *
+ * This file is part of liza.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+var AbstractClass = require( 'easejs' ).AbstractClass;
+
+
+/**
+ * Style fields using CSS
+ */
+module.exports = AbstractClass( 'FieldStyler',
+{
+ 'abstract public getId': [],
+
+ 'abstract public applyStyle': [ 'field', 'element', 'row' ],
+
+ 'abstract public revokeStyle': [ 'field', 'element', 'row' ],
+
+
+ 'protected addClass': function( element, cls )
+ {
+ if ( !element )
+ {
+ return this;
+ }
+
+ // if we are given an array, then recurse
+ if ( Array.isArray( element ) )
+ {
+ for ( var i in element )
+ {
+ this.addClass( element[ i ], cls );
+ }
+
+ return;
+ }
+ else if ( typeof element.className === 'string' )
+ {
+ element.className += ' ' + cls;
+ }
+
+ return this;
+ },
+
+
+ 'protected removeClass': function( element, cls )
+ {
+ if ( !element )
+ {
+ return this;
+ }
+
+ // if we are given an array, then recurse
+ if ( Array.isArray( element ) )
+ {
+ for ( var i in element )
+ {
+ this.removeClass( element[ i ], cls );
+ }
+
+ return;
+ }
+ else if ( typeof element.className === 'string' )
+ {
+ // note that we use a space instead of a boundary for the character
+ // preceding the match due to the implementation of addClass()
+ element.className = element.className.replace(
+ new RegExp( ( ' ' + cls + '\\b' ), 'g' ), ''
+ );
+ }
+
+ return this;
+ }
+} );
diff --git a/src/ui/styler/SidebarErrorStyler.js b/src/ui/styler/SidebarErrorStyler.js
new file mode 100644
index 0000000..c301ab3
--- /dev/null
+++ b/src/ui/styler/SidebarErrorStyler.js
@@ -0,0 +1,66 @@
+/**
+ * Style errors in sidebar
+ *
+ * Copyright (C) 2016 LoVullo Associates, Inc.
+ *
+ * This file is part of liza.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+var Class = require( 'easejs' ).Class,
+ ErrorStyler = require( './ErrorStyler' );
+
+
+/**
+ * Displays errors in the sidebar
+ *
+ * TODO: This is an adapter around the old system; it could use some
+ * refactoring.
+ */
+module.exports = Class( 'SidebarErrorStyler' )
+ .extend( ErrorStyler,
+{
+ /**
+ * Error box in which to display errors
+ * @type {FormErrorBox}
+ */
+ 'private _errbox': null,
+
+ /**
+ * Ui instance
+ * @type {Ui}
+ */
+ 'private _ui': null,
+
+
+ 'override __construct': function( msgs, error_box, ui )
+ {
+ this._errbox = error_box;
+ this._ui = ui;
+ this.__super( msgs );
+ },
+
+
+ 'override protected onFieldError': function( field, msg )
+ {
+ this._errbox.show( field.getName(), field.getIndex(), msg );
+ },
+
+
+ 'override protected onFieldFixed': function( field )
+ {
+ this._errbox.removeError( field.getName(), field.getIndex() );
+ }
+} );
diff --git a/src/ui/styler/StepErrorStyler.js b/src/ui/styler/StepErrorStyler.js
new file mode 100644
index 0000000..83b344c
--- /dev/null
+++ b/src/ui/styler/StepErrorStyler.js
@@ -0,0 +1,52 @@
+/**
+ * Styles errors on steps
+ *
+ * Copyright (C) 2016 LoVullo Associates, Inc.
+ *
+ * This file is part of liza.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+var Class = require( 'easejs' ).Class,
+ ErrorStyler = require( './ErrorStyler' );
+
+
+/**
+ * Trigger field styling for errors on the parent step itself
+ */
+module.exports = Class( 'StepErrorStyler' )
+ .extend( ErrorStyler,
+{
+ 'private _style': null,
+
+
+ 'override __construct': function( msgs, field_style )
+ {
+ this._style = field_style;
+ this.__super( msgs );
+ },
+
+
+ 'override protected onFieldError': function( field, msg )
+ {
+ field.applyStyle( this._style, msg );
+ },
+
+
+ 'override protected onFieldFixed': function( field )
+ {
+ field.revokeStyle( this._style );
+ }
+} );
diff --git a/src/ui/styler/Styler.js b/src/ui/styler/Styler.js
new file mode 100644
index 0000000..bfb144e
--- /dev/null
+++ b/src/ui/styler/Styler.js
@@ -0,0 +1,30 @@
+/**
+ * Styler interface
+ *
+ * Copyright (C) 2016 LoVullo Associates, Inc.
+ *
+ * This file is part of liza.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+var Interface = require( 'easejs' ).Interface;
+
+
+/**
+ * @todo Can be used for type hinting, but we need an actual API!
+ */
+module.exports = Interface( 'Styler',
+{
+} );