diff --git a/packages/boundless-input/README.md b/packages/boundless-input/README.md
index 724234ef..8d2196af 100644
--- a/packages/boundless-input/README.md
+++ b/packages/boundless-input/README.md
@@ -79,6 +79,12 @@ There are no required props.
 
 ### Optional Props
 
+- __`component`__ ・ overrides the HTML container tag
+
+  Expects | Default Value
+  -       | -
+  `string` | `'div'`
+
 - __`hidePlaceholderOnFocus`__ ・ triggers the placeholder to disappear when the input field is focused, reappears when the user has tabbed away or focus is moved
 
   Expects | Default Value
diff --git a/packages/boundless-input/index.js b/packages/boundless-input/index.js
index 6d62d712..eb072c30 100644
--- a/packages/boundless-input/index.js
+++ b/packages/boundless-input/index.js
@@ -23,6 +23,11 @@ When using `Input` in your project, you may call the following methods on a rend
  */
 export default class Input extends React.PureComponent {
     static propTypes = {
+        /**
+         * overrides the HTML container tag
+         */
+        component: PropTypes.string,
+
         /**
          * triggers the placeholder to disappear when the input field is focused, reappears when the user has tabbed away or focus is moved
          */
@@ -43,6 +48,7 @@ export default class Input extends React.PureComponent {
     }
 
     static defaultProps = {
+        component: 'div',
         hidePlaceholderOnFocus: true,
         inputProps: {
             type: 'text',
@@ -118,35 +124,32 @@ export default class Input extends React.PureComponent {
 
     getPlaceholderText() {
         const isNonEmpty = this.state.input !== '';
-        const shouldShowPlaceholder =   this.props.hidePlaceholderOnFocus === true
-                                        ? this.state.isFocused === false && isNonEmpty === false
-                                        : isNonEmpty === false;
+        const shouldShowPlaceholder = this.props.hidePlaceholderOnFocus === true
+                                      ? this.state.isFocused === false && isNonEmpty === false
+                                      : isNonEmpty === false;
 
         return shouldShowPlaceholder ? this.props.inputProps.placeholder : '';
     }
 
     render() {
-        const {props} = this;
-
         return (
-            <div
-                {...omit(props, Input.internalKeys)}
-                ref='wrapper'
-                className={cx('b-input-wrapper', props.className)}
+            <this.props.component
+                {...omit(this.props, Input.internalKeys)}
+                className={cx('b-input-wrapper', this.props.className)}
                 title={this.getPlaceholderText()}>
                 <input
-                    {...props.inputProps}
+                    {...this.props.inputProps}
                     ref='field'
-                    className={cx('b-input', props.inputProps.className)}
+                    className={cx('b-input', this.props.inputProps.className)}
                     placeholder={null}
                     onBlur={this.handleBlur}
                     onFocus={this.handleFocus}
                     onChange={this.handleChange} />
 
-                <div ref='placeholder' className='b-input-placeholder b-input'>
+                <div className='b-input-placeholder b-input'>
                     {this.getPlaceholderText()}
                 </div>
-            </div>
+            </this.props.component>
         );
     }
 }
diff --git a/packages/boundless-input/index.spec.js b/packages/boundless-input/index.spec.js
index b0b806be..4be78d8f 100644
--- a/packages/boundless-input/index.spec.js
+++ b/packages/boundless-input/index.spec.js
@@ -3,10 +3,9 @@
 import React from 'react';
 import ReactDOM from 'react-dom';
 import sinon from 'sinon';
-import {noop} from 'lodash';
 
 import Input from './index';
-import {conformanceChecker} from '../boundless-utils-test-helpers/index';
+import {$, conformanceChecker} from '../boundless-utils-test-helpers/index';
 
 describe('Input component', () => {
     const mountNode = document.body.appendChild(document.createElement('div'));
@@ -29,127 +28,66 @@ describe('Input component', () => {
         conformanceChecker(render, Input, props);
     });
 
-    describe('accepts', () => {
-        it('arbitrary HTML attributes via props.inputProps', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        'data-id': 'foo',
-                    }} />
-            );
-
-            expect(element.refs.field.getAttribute('data-id')).toBe('foo');
-        });
-
-        it('additional classes via props.inputProps.className', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        className: 'foo',
-                    }} />
-            );
-
-            expect(element.refs.field.classList.contains('foo')).toBe(true);
-        });
+    it('renders .b-input-wrapper', () => {
+        render(<Input {...props} />);
+        expect($('.b-input-wrapper')).not.toBeNull();
     });
 
-    describe('CSS hook', () => {
-        const hasClass = (dom, name) => dom.classList.contains(name);
-
-        it('renders .b-input-wrapper', () => {
-            const element = render(<Input {...props} />);
-
-            expect(hasClass(element.refs.wrapper, 'b-input-wrapper')).toBe(true);
-        });
+    it('renders .b-input', () => {
+        render(<Input {...props} />);
+        expect($('.b-input')).not.toBeNull();
+    });
 
-        it('renders .b-input', () => {
-            const element = render(<Input {...props} />);
+    it('renders .b-input-placeholder', () => {
+        render(<Input {...props} inputProps={{placeholder: 'foo'}} />);
+        expect($('.b-input-placeholder')).not.toBeNull();
+    });
 
-            expect(hasClass(element.refs.field, 'b-input')).toBe(true);
-        });
+    it('accepts a different wrapper component', () => {
+        render(<Input {...props} component='article' />);
+        expect($('article.b-input-wrapper')).not.toBeNull();
     });
 
-    it('renders the placeholder facsimile', () => {
-        const element = render(<Input {...props} />);
+    it('accepts arbitrary HTML attributes via props.inputProps', () => {
+        render(<Input {...props} inputProps={{'data-id': 'foo'}} />);
+        expect($('.b-input[data-id="foo"]')).not.toBeNull();
+    });
 
-        expect(element.refs.placeholder).not.toBeUndefined();
-        expect(element.refs.placeholder.classList.contains('b-input-placeholder')).toBe(true);
+    it('accepts CSS classes via props.inputProps', () => {
+        render(<Input {...props} inputProps={{className: 'foo'}} />);
+        expect($('.b-input.foo')).not.toBeNull();
     });
 
     it('uses the proper placeholder text (via props.inputProps.placeholder)', () => {
-        const element = render(
-            <Input
-                {...props}
-                inputProps={{
-                    ...props.inputProps,
-                    placeholder: 'foo',
-                }} />
-        );
-
-        expect(element.refs.placeholder.textContent).toBe('foo');
+        render(<Input {...props} inputProps={{placeholder: 'foo'}} />);
+        expect($('.b-input-placeholder').textContent).toBe('foo');
     });
 
     it('does not empty the placeholder on input focus if `props.hidePlaceholderOnFocus` is false', () => {
-        const element = render(
-            <Input
-                {...props}
-                hidePlaceholderOnFocus={false}
-                inputProps={{
-                    ...props.inputProps,
-                    placeholder: 'foo',
-                }} />
-        );
-
-        expect(element.refs.placeholder).not.toBeUndefined();
-        expect(element.refs.placeholder.textContent).toBe('foo');
+        const element = render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo'}} />);
+        expect($('.b-input-placeholder').textContent).toBe('foo');
 
         element.handleFocus();
-
-        expect(element.refs.placeholder.textContent).toBe('foo');
+        expect($('.b-input-placeholder').textContent).toBe('foo');
     });
 
     it('empties the placeholder on input focus if `props.hidePlaceholderOnFocus` is true', () => {
-        const element = render(
-            <Input
-                {...props}
-                hidePlaceholderOnFocus={true}
-                inputProps={{
-                    ...props.inputProps,
-                    placeholder: 'foo',
-                }} />
-        );
-
-        expect(element.refs.placeholder).not.toBeUndefined();
-        expect(element.refs.placeholder.textContent).toBe('foo');
+        const element = render(<Input {...props} hidePlaceholderOnFocus={true} inputProps={{placeholder: 'foo'}} />);
+        expect($('.b-input-placeholder').textContent).toBe('foo');
 
         element.handleFocus();
-
-        expect(element.refs.placeholder.textContent).toBe('');
+        expect($('.b-input-placeholder').textContent).toBe('');
     });
 
     it('fills in the placeholder on input blur if the the input is empty and `props.hidePlaceholderOnFocus` is true', () => {
-        const element = render(
-            <Input
-                {...props}
-                hidePlaceholderOnFocus={true}
-                inputProps={{
-                    ...props.inputProps,
-                    placeholder: 'foo',
-                }} />
-        );
-
-        expect(element.refs.placeholder).not.toBeUndefined();
-        expect(element.refs.placeholder.textContent).toBe('foo');
+        const element = render(<Input {...props} hidePlaceholderOnFocus={true} inputProps={{placeholder: 'foo'}} />);
+        expect($('.b-input-placeholder').textContent).toBe('foo');
 
         element.handleFocus();
-        expect(element.refs.placeholder.textContent).toBe('');
+        expect($('.b-input-placeholder').textContent).toBe('');
 
         element.handleBlur();
-        expect(element.refs.placeholder.textContent).toBe('foo');
+        expect($('.b-input-placeholder').textContent).toBe('foo');
     });
 
     describe('controlled mode', () => {
@@ -157,192 +95,81 @@ describe('Input component', () => {
         beforeEach(() => sandbox.stub(console, 'error'));
 
         it('causes the placeholder to be filled in when the input is empty', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'foo',
-                        value: '',
-                    }} />
-            );
-
-            expect(element.refs.placeholder.textContent).toBe('foo');
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo', value: ''}} />);
+            expect($('.b-input-placeholder').textContent).toBe('foo');
         });
 
-        it('causes the placeholder to be empty when the input is non-empty', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'foo',
-                        value: 'x',
-                    }} />
-            );
-
-            expect(element.refs.placeholder.textContent).toBe('');
+        it('causes the placeholder to be emptied when the input has a value', () => {
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo', value: 'x'}} />);
+            expect($('.b-input-placeholder').textContent).toBe('');
         });
 
         it('properly manages placeholder visibility across many `props.inputProps.value` changes', () => {
-            let element;
-
-            element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        onChange: noop,
-                        placeholder: 'foo',
-                        value: 'x',
-                    }} />
-            );
-            expect(element.refs.placeholder.textContent).toBe('');
-
-            element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        onChange: noop,
-                        placeholder: 'foo',
-                        value: '',
-                    }} />
-            );
-            expect(element.refs.placeholder.textContent).toBe('foo');
-
-            element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        onChange: noop,
-                        placeholder: 'foo',
-                        value: 'x',
-                    }} />
-            );
-            expect(element.refs.placeholder.textContent).toBe('');
-
-            element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        onChange: noop,
-                        placeholder: 'foo',
-                        value: 'xy',
-                    }} />
-            );
-            expect(element.refs.placeholder.textContent).toBe('');
-
-            element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        onChange: noop,
-                        placeholder: 'foo',
-                        value: '',
-                    }} />
-            );
-            expect(element.refs.placeholder.textContent).toBe('foo');
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo', value: 'x'}} />);
+            expect($('.b-input-placeholder').textContent).toBe('');
+
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo', value: ''}} />);
+            expect($('.b-input-placeholder').textContent).toBe('foo');
+
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo', value: 'x'}} />);
+            expect($('.b-input-placeholder').textContent).toBe('');
+
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo', value: 'xy'}} />);
+            expect($('.b-input-placeholder').textContent).toBe('');
+
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo', value: ''}} />);
+            expect($('.b-input-placeholder').textContent).toBe('foo');
         });
 
-        it('changes to the input text result do not result in a setState within the event handler', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'foo',
-                        value: '',
-                    }} />
-            );
+        it('change events on the input are ignored and proxied to the composer', () => {
+            const changeStub = sandbox.stub();
+            const element = render(<Input {...props} inputProps={{onChange: changeStub, placeholder: 'foo', value: ''}} />);
 
-            sandbox.stub(element, 'setState');
+            sandbox.spy(element, 'setState');
 
             element.handleChange({target: {value: 'foobar'}});
             expect(element.setState.called).toBe(false);
+            expect(changeStub.calledOnce).toBe(true);
         });
     });
 
     describe('uncontrolled mode', () => {
         it('causes the placeholder to be filled in when the input is empty', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'foo',
-                    }} />
-            );
-
-            expect(element.refs.placeholder.textContent).toBe('foo');
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo'}} />);
+            expect($('.b-input-placeholder').textContent).toBe('foo');
         });
 
         it('causes the placeholder to be empty when given `inputProps.defaultValue`', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'foo',
-                        defaultValue: 'foo',
-                    }} />
-            );
-
-            expect(element.refs.placeholder.textContent).toBe('');
+            render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{defaultValue: 'foo', placeholder: 'foo'}} />);
+            expect($('.b-input-placeholder').textContent).toBe('');
         });
 
         it('causes the placeholder to be empty when the input is non-empty', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'foo',
-                    }} />
-            );
+            const element = render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo'}} />);
 
             element.handleChange({target: {value: 'x'}});
-
-            expect(element.refs.placeholder.textContent).toBe('');
+            expect($('.b-input-placeholder').textContent).toBe('');
         });
 
         it('properly manages placeholder visibility across many input changes', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'foo',
-                    }} />
-            );
+            const element = render(<Input {...props} hidePlaceholderOnFocus={false} inputProps={{placeholder: 'foo'}} />);
 
-            expect(element.refs.placeholder.textContent).toBe('foo');
+            expect($('.b-input-placeholder').textContent).toBe('foo');
 
             element.handleChange({target: {value: 'x'}});
-            expect(element.refs.placeholder.textContent).toBe('');
+            expect($('.b-input-placeholder').textContent).toBe('');
 
             element.handleChange({target: {value: 'xy'}});
-            expect(element.refs.placeholder.textContent).toBe('');
+            expect($('.b-input-placeholder').textContent).toBe('');
 
             element.handleChange({target: {value: ''}});
-            expect(element.refs.placeholder.textContent).toBe('foo');
+            expect($('.b-input-placeholder').textContent).toBe('foo');
         });
     });
 
     describe('getValue()', () => {
         it('returns the current value of the input field', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        defaultValue: 'bar',
-                        placeholder: 'foo',
-                    }} />
-            );
+            const element = render(<Input {...props} inputProps={{defaultValue: 'bar'}} />);
 
             expect(element.getValue()).toBe('bar');
         });
@@ -360,71 +187,38 @@ describe('Input component', () => {
 
         it('triggers the inputProps.onChange flow before the value is reset by React for a controlled component', () => {
             const changeStub = sandbox.stub();
-
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        onChange: changeStub,
-                        value: 'ap',
-                    }} />
-            );
+            const element = render(<Input {...props} inputProps={{onChange: changeStub, value: 'ap'}} />);
 
             element.setValue('foo');
             expect(changeStub.calledOnce).toBe(true);
         });
 
         it('empties the placeholder if set with a non-empty string', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        placeholder: 'bar',
-                    }} />
-            );
+            const element = render(<Input {...props} inputProps={{placeholder: 'bar'}} />);
 
             expect(element.getValue()).toBe('');
-            expect(element.refs.placeholder.textContent).toBe('bar');
+            expect($('.b-input-placeholder').textContent).toBe('bar');
 
             element.setValue('foo');
             expect(element.getValue()).toBe('foo');
-            expect(element.refs.placeholder.textContent).toBe('');
+            expect($('.b-input-placeholder').textContent).toBe('');
         });
 
         it('restores the placeholder if set with an empty string', () => {
-            const element = render(
-                <Input
-                    {...props}
-                    inputProps={{
-                        ...props.inputProps,
-                        defaultValue: 'foo',
-                        placeholder: 'bar',
-                    }} />
-            );
+            const element = render(<Input {...props} inputProps={{defaultValue: 'foo', placeholder: 'bar'}} />);
 
             expect(element.getValue()).toBe('foo');
-            expect(element.refs.placeholder.textContent).toBe('');
+            expect($('.b-input-placeholder').textContent).toBe('');
 
             element.setValue('');
             expect(element.getValue()).toBe('');
-            expect(element.refs.placeholder.textContent).toBe('bar');
+            expect($('.b-input-placeholder').textContent).toBe('bar');
         });
     });
 
     it('proxies input events to `props.inputProps.onBlur` if provided', () => {
         const stub = sandbox.stub();
-
-        const element = render(
-            <Input
-                {...props}
-                inputProps={{
-                    ...props.inputProps,
-                    onBlur: stub,
-                }} />
-        );
-
+        const element = render(<Input {...props} inputProps={{onBlur: stub}} />);
         const event = {};
 
         element.handleBlur(event);
@@ -435,16 +229,7 @@ describe('Input component', () => {
 
     it('proxies input events to `props.inputProps.onFocus` if provided', () => {
         const stub = sandbox.stub();
-
-        const element = render(
-            <Input
-                {...props}
-                inputProps={{
-                    ...props.inputProps,
-                    onFocus: stub,
-                }} />
-        );
-
+        const element = render(<Input {...props} inputProps={{onFocus: stub}} />);
         const event = {};
 
         element.handleFocus(event);
@@ -455,16 +240,7 @@ describe('Input component', () => {
 
     it('proxies input events to `props.inputProps.onChange` if provided', () => {
         const stub = sandbox.stub();
-
-        const element = render(
-            <Input
-                {...props}
-                inputProps={{
-                    ...props.inputProps,
-                    onChange: stub,
-                }} />
-        );
-
+        const element = render(<Input {...props} inputProps={{onChange: stub}} />);
         const event = {target: {value: 'x'}};
 
         element.handleChange(event);
diff --git a/packages/boundless-tokenized-input/README.md b/packages/boundless-tokenized-input/README.md
index 5da1daa9..5779c492 100644
--- a/packages/boundless-tokenized-input/README.md
+++ b/packages/boundless-tokenized-input/README.md
@@ -358,6 +358,12 @@ There are no required props.
   -       | -
   `bool` | `false`
 
+- __`component`__ ・ overrides the HTML container tag
+
+  Expects | Default Value
+  -       | -
+  `string` | `'div'`
+
 - __`entities`__ ・ an array of objects that user input is filtered against; at a minimum, each object must have a `text` property and any other supplied property is passed through to the resulting DOM element
 
   Expects | Default Value
diff --git a/packages/boundless-typeahead/README.md b/packages/boundless-typeahead/README.md
index 1a082694..24bb6b73 100644
--- a/packages/boundless-typeahead/README.md
+++ b/packages/boundless-typeahead/README.md
@@ -397,6 +397,12 @@ There are no required props.
   -       | -
   `bool` | `false`
 
+- __`component`__ ・ overrides the HTML container tag
+
+  Expects | Default Value
+  -       | -
+  `string` | `'div'`
+
 - __`entities`__ ・ an array of objects that user input is filtered against; at a minimum, each object must have a `text` property and any other supplied property is passed through to the resulting DOM element
 
   Expects | Default Value