diff --git a/packages/core/src/components/input/input.ios.scss b/packages/core/src/components/input/input.ios.scss
new file mode 100644
index 0000000000..a0898f7375
--- /dev/null
+++ b/packages/core/src/components/input/input.ios.scss
@@ -0,0 +1,182 @@
+@import "../../themes/ionic.globals.ios";
+@import "./input";
+
+
+// iOS Input
+// --------------------------------------------------
+
+/// @prop - Background color of the input
+$text-input-ios-background-color: $list-ios-background-color !default;
+
+/// @prop - Margin top of the input
+$text-input-ios-margin-top: $item-ios-padding-top !default;
+
+// deprecated
+$text-input-ios-margin-right: ($item-ios-padding-end / 2) !default;
+/// @prop - Margin end of the input
+$text-input-ios-margin-end: $text-input-ios-margin-right !default;
+
+/// @prop - Margin bottom of the input
+$text-input-ios-margin-bottom: $item-ios-padding-bottom !default;
+
+// deprecated
+$text-input-ios-margin-left: 0 !default;
+/// @prop - Margin start of the input
+$text-input-ios-margin-start: $text-input-ios-margin-left !default;
+
+/// @prop - Width of the icon used to clear the input
+$text-input-ios-input-clear-icon-width: 30px !default;
+
+/// @prop - Color of the icon used to clear the input
+$text-input-ios-input-clear-icon-color: rgba(0, 0, 0, .5) !default;
+
+/// @prop - Icon used to clear the input
+$text-input-ios-input-clear-icon-svg: "" !default;
+
+/// @prop - Size of the icon used to clear the input
+$text-input-ios-input-clear-icon-size: 18px !default;
+
+/// @prop - Show the focus highlight when the input has focus
+$text-input-ios-show-focus-highlight: false !default;
+
+/// @prop - Show the valid highlight when it is valid and has a value
+$text-input-ios-show-valid-highlight: $text-input-ios-show-focus-highlight !default;
+
+/// @prop - Show the invalid highlight when it is invalid and has value
+$text-input-ios-show-invalid-highlight: $text-input-ios-show-focus-highlight !default;
+
+/// @prop - Color of the input highlight
+$text-input-ios-highlight-color: color($colors-ios, primary) !default;
+
+/// @prop - Color of the input highlight when valid
+$text-input-ios-highlight-color-valid: $text-input-highlight-color-valid !default;
+
+/// @prop - Color of the input highlight when invalid
+$text-input-ios-highlight-color-invalid: $text-input-highlight-color-invalid !default;
+
+
+// iOS Default Input
+// --------------------------------------------------
+
+.text-input-ios {
+ @include margin($text-input-ios-margin-top, $text-input-ios-margin-end, $text-input-ios-margin-bottom, $text-input-ios-margin-start);
+ @include padding(0);
+
+ width: calc(100% - #{($text-input-ios-margin-end + $text-input-ios-margin-start)});
+}
+
+
+// iOS Inset Input
+// --------------------------------------------------
+
+.input-ios .inset-input {
+ @include padding(($item-ios-padding-top / 2), ($item-ios-padding-end / 2), ($item-ios-padding-bottom / 2), ($item-ios-padding-start / 2));
+ @include margin(($item-ios-padding-top / 2), $item-ios-padding-end, ($item-ios-padding-bottom / 2), 0);
+}
+
+
+// iOS Highlighted Input
+// --------------------------------------------------
+
+// Input highlight mixin for focus, valid, and invalid states
+@mixin ios-input-highlight($highlight-color) {
+ border-bottom-color: $highlight-color;
+}
+
+// Show the focus highlight when the input has focus
+@if ($text-input-ios-show-focus-highlight) {
+ // In order to get a 2px border we need to add an inset
+ // box-shadow 1px (this is to avoid the div resizing)
+
+ // TODO remove all uses of input-has-focus in v4
+ // TODO remove all uses of input-has-value in v4
+ .item-ios.item-input.item-input-has-focus .item-inner,
+ .item-ios.item-input.input-has-focus .item-inner {
+ @include ios-input-highlight($text-input-ios-highlight-color);
+ }
+
+ // The last item in a list has a border on the item, not the
+ // inner item, so add it to the item itself
+ .list-ios .item-input.item-input-has-focus:last-child,
+ .list-ios .item-input.input-has-focus:last-child {
+ @include ios-input-highlight($text-input-ios-highlight-color);
+
+ .item-inner {
+ box-shadow: none;
+ }
+ }
+}
+
+// Show the valid highlight when it has the .ng-valid class and a value
+@if ($text-input-ios-show-valid-highlight) {
+ .item-ios.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner,
+ .item-ios.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
+ @include ios-input-highlight($text-input-ios-highlight-color-valid);
+ }
+
+ .list-ios .item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child,
+ .list-ios .item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child {
+ @include ios-input-highlight($text-input-ios-highlight-color-valid);
+
+ .item-inner {
+ box-shadow: none;
+ }
+ }
+}
+
+// Show the invalid highlight when it has the invalid class and has been touched
+@if ($text-input-ios-show-invalid-highlight) {
+ .item-ios.item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
+ @include ios-input-highlight($text-input-ios-highlight-color-invalid);
+ }
+
+ .list-ios .item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus):last-child {
+ @include ios-input-highlight($text-input-ios-highlight-color-invalid);
+
+ .item-inner {
+ box-shadow: none;
+ }
+ }
+}
+
+
+// iOS Stacked & Floating Inputs
+// --------------------------------------------------
+
+.item-ios.item-label-stacked .text-input,
+.item-ios.item-label-floating .text-input {
+ @include margin(8px, null, 8px, 0);
+
+ width: calc(100% - #{$text-input-ios-margin-end});
+}
+
+.item-ios.item-label-stacked .label-ios + .input + .cloned-input,
+.item-ios.item-label-floating .label-ios + .input + .cloned-input {
+ @include margin-horizontal(0, null);
+}
+
+.item-label-stacked .select-ios,
+.item-label-floating .select-ios {
+ @include padding(8px, null, 8px, 0);
+}
+
+
+// iOS Clear Input Icon
+// --------------------------------------------------
+
+.input-ios[clearInput] {
+ position: relative;
+}
+
+.input-ios[clearInput] .text-input {
+ @include padding-horizontal(null, $text-input-ios-input-clear-icon-width);
+}
+
+.input-ios .text-input-clear-icon {
+ @include position-horizontal(null, ($item-ios-padding-end / 2));
+ @include svg-background-image($text-input-ios-input-clear-icon-svg);
+
+ width: $text-input-ios-input-clear-icon-width;
+
+ background-size: $text-input-ios-input-clear-icon-size;
+}
diff --git a/packages/core/src/components/input/input.md.scss b/packages/core/src/components/input/input.md.scss
new file mode 100644
index 0000000000..a82b29429d
--- /dev/null
+++ b/packages/core/src/components/input/input.md.scss
@@ -0,0 +1,179 @@
+@import "../../themes/ionic.globals.md";
+@import "./input";
+
+
+// Material Design Input
+// --------------------------------------------------
+
+/// @prop - Background color of the input
+$text-input-md-background-color: $list-md-background-color !default;
+
+/// @prop - Margin top of the input
+$text-input-md-margin-top: $item-md-padding-top !default;
+
+// deprecated
+$text-input-md-margin-right: ($item-md-padding-end / 2) !default;
+/// @prop - Margin end of the input
+$text-input-md-margin-end: $text-input-md-margin-right !default;
+
+/// @prop - Margin bottom of the input
+$text-input-md-margin-bottom: $item-md-padding-bottom !default;
+
+// deprecated
+$text-input-md-margin-left: ($item-md-padding-start / 2) !default;
+/// @prop - Margin start of the input
+$text-input-md-margin-start: $text-input-md-margin-left !default;
+
+/// @prop - Width of the icon used to clear the input
+$text-input-md-input-clear-icon-width: 30px !default;
+
+/// @prop - Color of the icon used to clear the input
+$text-input-md-input-clear-icon-color: #5b5b5b !default;
+
+/// @prop - Icon used to clear the input
+$text-input-md-input-clear-icon-svg: "" !default;
+
+/// @prop - Size of the icon used to clear the input
+$text-input-md-input-clear-icon-size: 22px !default;
+
+/// @prop - Show the focus highlight when the input has focus
+$text-input-md-show-focus-highlight: true !default;
+
+/// @prop - Show the valid highlight when it is valid and has a value
+$text-input-md-show-valid-highlight: $text-input-md-show-focus-highlight !default;
+
+/// @prop - Show the invalid highlight when it is invalid and has value
+$text-input-md-show-invalid-highlight: $text-input-md-show-focus-highlight !default;
+
+/// @prop - Color of the input highlight
+$text-input-md-highlight-color: color($colors-md, primary) !default;
+
+/// @prop - Color of the input highlight when valid
+$text-input-md-highlight-color-valid: $text-input-highlight-color-valid !default;
+
+/// @prop - Color of the input highlight when invalid
+$text-input-md-highlight-color-invalid: $text-input-highlight-color-invalid !default;
+
+
+// Material Design Default Input
+// --------------------------------------------------
+
+.text-input-md {
+ @include margin($text-input-md-margin-top, $text-input-md-margin-end, $text-input-md-margin-bottom, $text-input-md-margin-start);
+ @include padding(0);
+
+ width: calc(100% - #{$text-input-md-margin-end} - #{$text-input-md-margin-start});
+}
+
+
+// Material Design Inset Input
+// --------------------------------------------------
+
+.input-md .inset-input {
+ @include padding(($item-md-padding-top / 2), ($item-md-padding-end / 2), ($item-md-padding-bottom / 2), ($item-md-padding-start / 2));
+ @include margin(($item-md-padding-top / 2), $item-md-padding-end, ($item-md-padding-bottom / 2), $item-md-padding-start);
+}
+
+
+// Material Design Highlighted Input
+// --------------------------------------------------
+
+// Input highlight mixin for focus, valid, and invalid states
+@mixin md-input-highlight($highlight-color) {
+ border-bottom-color: $highlight-color;
+ box-shadow: inset 0 -1px 0 0 $highlight-color;
+}
+
+// Show the focus highlight when the input has focus
+@if ($text-input-md-show-focus-highlight) {
+ // In order to get a 2px border we need to add an inset
+ // box-shadow 1px (this is to avoid the div resizing)
+
+ // TODO remove all uses of input-has-focus in v4
+ .item-md.item-input.item-input-has-focus .item-inner,
+ .item-md.item-input.input-has-focus .item-inner {
+ @include md-input-highlight($text-input-md-highlight-color);
+ }
+
+ // The last item in a list has a border on the item, not the
+ // inner item, so add it to the item itself
+ .list-md .item-input.item-input-has-focus:last-child,
+ .list-md .item-input.input-has-focus:last-child {
+ @include md-input-highlight($text-input-md-highlight-color);
+
+ .item-inner {
+ box-shadow: none;
+ }
+ }
+}
+
+// Show the valid highlight when it has the .ng-valid class and a value
+@if ($text-input-md-show-valid-highlight) {
+ // TODO remove all uses of input-has-focus in v4
+ // TODO remove all uses of input-has-value in v4
+ .item-md.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner,
+ .item-md.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
+ @include md-input-highlight($text-input-md-highlight-color-valid);
+ }
+
+ .list-md .item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child,
+ .list-md .item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child {
+ @include md-input-highlight($text-input-md-highlight-color-valid);
+
+ .item-inner {
+ box-shadow: none;
+ }
+ }
+}
+
+// Show the invalid highlight when it has the invalid class and has been touched
+@if ($text-input-md-show-invalid-highlight) {
+ .item-md.item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
+ @include md-input-highlight($text-input-md-highlight-color-invalid);
+ }
+
+ .list-md .item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus):last-child {
+ @include md-input-highlight($text-input-md-highlight-color-invalid);
+
+ .item-inner {
+ box-shadow: none;
+ }
+ }
+}
+
+
+// Material Design Stacked & Floating Inputs
+// --------------------------------------------------
+
+.item-label-stacked .text-input-md,
+.item-label-floating .text-input-md {
+ @include margin(8px, null, 8px, 0);
+
+ width: calc(100% - #{$text-input-md-margin-end});
+}
+
+.item-label-stacked .select-md,
+.item-label-floating .select-md {
+ @include padding(8px, null, 8px, 0);
+}
+
+
+// Material Design Clear Input Icon
+// --------------------------------------------------
+
+.input-md[clearInput] {
+ position: relative;
+}
+
+.input-md[clearInput] .text-input {
+ @include padding-horizontal(null, $text-input-md-input-clear-icon-width);
+}
+
+.input-md .text-input-clear-icon {
+ @include position-horizontal(null, ($item-md-padding-end / 2));
+ @include svg-background-image($text-input-md-input-clear-icon-svg);
+
+ width: $text-input-md-input-clear-icon-width;
+
+ background-size: $text-input-md-input-clear-icon-size;
+}
diff --git a/packages/core/src/components/input/input.scss b/packages/core/src/components/input/input.scss
new file mode 100644
index 0000000000..bc284ab90c
--- /dev/null
+++ b/packages/core/src/components/input/input.scss
@@ -0,0 +1,165 @@
+@import "../../themes/ionic.globals";
+
+// Input
+// --------------------------------------------------
+
+/// @prop - Color of the input highlight when valid
+$text-input-highlight-color-valid: #32db64 !default;
+
+/// @prop - Color of the input highlight when invalid
+$text-input-highlight-color-invalid: #f53d3d !default;
+
+/// @prop - Color of the input placeholder
+$text-input-placeholder-color: #999 !default;
+
+
+// Input/Textarea Wrapper
+// --------------------------------------------------
+
+ion-input,
+ion-textarea {
+ position: relative;
+ display: block;
+
+ flex: 1;
+
+ width: 100%;
+}
+
+.item-input ion-input,
+.item-input ion-textarea {
+ position: static;
+}
+
+
+// Textarea Within An Item
+// --------------------------------------------------
+
+.item.item-textarea {
+ align-items: stretch;
+}
+
+
+// Native Text Input
+// --------------------------------------------------
+
+.text-input {
+ @include placeholder($text-input-placeholder-color);
+ @include appearance(none);
+ @include border-radius(0);
+
+ display: inline-block;
+
+ flex: 1;
+
+ width: 92%;
+ width: calc(100% - 10px);
+
+ border: 0;
+
+ background: transparent;
+}
+
+textarea.text-input {
+ display: block;
+}
+
+.text-input[disabled] {
+ opacity: .4;
+}
+
+input.text-input:-webkit-autofill {
+ background-color: transparent;
+}
+
+.platform-mobile textarea.text-input {
+ resize: none;
+}
+
+
+// Input Cover: Unfocused
+// --------------------------------------------------
+// The input cover is the div that actually receives the
+// tap/click event when scroll assist is configured to true.
+// This make it so the native input element is not clickable.
+// This will only show when the scroll assist is configured
+// otherwise the .input-cover will not be rendered at all
+// The input cover is not clickable when the input is disabled
+
+.input-cover {
+ @include position(0, null, null, 0);
+
+ position: absolute;
+
+ width: 100%;
+ height: 100%;
+}
+
+.input[disabled] .input-cover {
+ pointer-events: none;
+}
+
+
+// Input Cover: Focused
+// --------------------------------------------------
+// When the input has focus, then the input cover should be hidden
+
+.item-input-has-focus .input-cover {
+ display: none;
+}
+
+.item-input-has-focus {
+ pointer-events: none;
+}
+
+.item-input-has-focus input,
+.item-input-has-focus textarea,
+.item-input-has-focus a,
+.item-input-has-focus button {
+ pointer-events: auto;
+}
+
+
+// Scroll Assist Input
+// --------------------------------------------------
+// This input is used to help the app handle
+// Next and Previous input tabbing
+
+[next-input] {
+ @include padding(0);
+
+ position: absolute;
+ bottom: 20px;
+
+ width: 1px;
+ height: 1px;
+
+ border: 0;
+ background: transparent;
+
+ pointer-events: none;
+}
+
+
+// Clear Input Icon
+// --------------------------------------------------
+
+.text-input-clear-icon {
+ @include margin(0);
+ @include padding(0);
+ @include background-position(center);
+
+ position: absolute;
+ top: 0;
+ display: none;
+
+ height: 100%;
+
+ background-repeat: no-repeat;
+}
+
+// TODO remove all uses of input-has-focus in v4
+// TODO remove all uses of input-has-value in v4
+.item-input-has-focus.item-input-has-value .text-input-clear-icon {
+ display: block;
+}
diff --git a/packages/core/src/components/input/input.tsx b/packages/core/src/components/input/input.tsx
new file mode 100644
index 0000000000..4e159f2f5b
--- /dev/null
+++ b/packages/core/src/components/input/input.tsx
@@ -0,0 +1,289 @@
+import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core';
+
+import { createThemedClasses } from '../../utils/theme';
+
+
+@Component({
+ tag: 'ion-input',
+ styleUrls: {
+ ios: 'input.ios.scss',
+ md: 'input.md.scss',
+ wp: 'input.wp.scss'
+ },
+ host: {
+ theme: 'input'
+ }
+})
+export class Input {
+ mode: any;
+ color: any;
+ styleTmr: any;
+ didBlurAfterEdit: boolean;
+
+ @Element() el: HTMLElement;
+
+ /**
+ * @output {event} Emitted when the styles change.
+ */
+ @Event() ionStyle: EventEmitter;
+
+ /**
+ * @output {event} Emitted when the input no longer has focus.
+ */
+ @Event() ionBlur: EventEmitter;
+
+ /**
+ * @output {event} Emitted when the input has focus.
+ */
+ @Event() ionFocus: EventEmitter;
+
+ /**
+ * @input {string} Indicates whether the value of the control can be automatically completed by the browser. Defaults to `"off"`.
+ */
+ @Prop() autocomplete: string = 'off';
+
+ /**
+ * @input {string} Whether autocorrection should be enabled when the user is entering/editing the text value. Defaults to `"off"`.
+ */
+ @Prop() autocorrect: string = 'off';
+
+ /**
+ * @input {string} This Boolean attribute lets you specify that a form control should have input focus when the page loads. Defaults to `false`.
+ */
+ @Prop() autofocus: boolean;
+
+ /**
+ * @input {boolean} If true and the type is `checkbox` or `radio`, the control is selected by default. Defaults to `false`.
+ */
+ @Prop() checked: boolean = false;
+
+ /**
+ * @hidden
+ */
+ @PropDidChange('checked')
+ setChecked() {
+ this.emitStyle();
+ }
+
+
+ /**
+ * @input {boolean} If true, a clear icon will appear in the input when there is a value. Clicking it clears the input. Defaults to `false`.
+ */
+ @Prop() clearInput: boolean = false;
+
+ /**
+ * @input {boolean} If true, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types. Defaults to `false`.
+ */
+ @Prop({state: true}) clearOnEdit: boolean;
+
+ /**
+ * @input {boolean} If true, the user cannot interact with this element. Defaults to `false`.
+ */
+ @Prop() disabled: boolean = false;
+
+ /**
+ * @hidden
+ */
+ @PropDidChange('disabled')
+ setDisabled() {
+ this.emitStyle();
+ }
+
+ /**
+ * @input {any} The minimum value, which must not be greater than its maximum (max attribute) value.
+ */
+ @Prop() min: string;
+
+ /**
+ * @input {any} The maximum value, which must not be less than its minimum (min attribute) value.
+ */
+ @Prop() max: string;
+
+ /**
+ * @input {string} Instructional text that shows before the input has a value.
+ */
+ @Prop() placeholder: string;
+
+ /**
+ * @input {boolean} If true, the user cannot modify the value. Defaults to `false`.
+ */
+ @Prop() readonly: boolean = false;
+
+ /**
+ * @input {string} If true, the element will have its spelling and grammar checked. Defaults to `false`.
+ */
+ @Prop() spellcheck: boolean = false;
+
+ /**
+ * @input {any} Works with the min and max attributes to limit the increments at which a value can be set.
+ */
+ @Prop() step: string;
+
+ /**
+ * @input {string} The type of control to display. The default type is text. Possible values are: `"text"`, `"password"`, `"email"`, `"number"`, `"search"`, `"tel"`, or `"url"`.
+ */
+ @Prop() type: string = 'text';
+
+ /**
+ * @input {string} The text value of the input.
+ */
+ @Prop({ state: true }) value: string;
+
+
+ ionViewDidLoad() {
+ this.emitStyle();
+
+ // By default, password inputs clear after focus when they have content
+ if (this.type === 'password' && this.clearOnEdit !== false) {
+ this.clearOnEdit = true;
+ }
+ }
+
+ private emitStyle() {
+ clearTimeout(this.styleTmr);
+
+ let styles = {
+ 'input': true,
+ 'input-checked': this.checked,
+ 'input-disabled': this.disabled,
+ 'input-has-value': this.hasValue(),
+ 'input-has-focus': this.hasFocus()
+ };
+
+ this.styleTmr = setTimeout(() => {
+ this.ionStyle.emit(styles);
+ });
+ }
+
+ /**
+ * @hidden
+ */
+ hasValue(): boolean {
+ return (this.value !== null && this.value !== undefined && this.value !== '');
+ }
+
+
+ /**
+ * @hidden
+ */
+ inputBlurred(ev: any) {
+ this.ionBlur.emit(ev);
+
+ this.focusChange(this.hasFocus());
+ this.emitStyle();
+ }
+
+
+ /**
+ * @hidden
+ */
+ inputChanged(ev: any) {
+ this.value = ev.target && ev.target.value;
+ this.emitStyle();
+ }
+
+
+ /**
+ * @hidden
+ */
+ inputFocused(ev: any) {
+ this.ionFocus.emit(ev);
+
+ this.focusChange(this.hasFocus());
+ this.emitStyle();
+ }
+
+
+ /**
+ * @hidden
+ */
+ hasFocus(): boolean {
+ // check if an input has focus or not
+ return this.el && (this.el.querySelector(':focus') === this.el.querySelector('input'));
+ }
+
+
+ /**
+ * @hidden
+ */
+ focusChange(inputHasFocus: boolean) {
+ // If clearOnEdit is enabled and the input blurred but has a value, set a flag
+ if (this.clearOnEdit && !inputHasFocus && this.hasValue()) {
+ this.didBlurAfterEdit = true;
+ }
+ }
+
+
+ /**
+ * @hidden
+ */
+ inputKeydown() {
+ this.checkClearOnEdit();
+ }
+
+
+ /**
+ * Check if we need to clear the text input if clearOnEdit is enabled
+ * @hidden
+ */
+ checkClearOnEdit() {
+ if (!this.clearOnEdit) {
+ return;
+ }
+
+ // Did the input value change after it was blurred and edited?
+ if (this.didBlurAfterEdit && this.hasValue()) {
+ // Clear the input
+ this.clearTextInput();
+ }
+
+ // Reset the flag
+ this.didBlurAfterEdit = false;
+ }
+
+
+ /**
+ * @hidden
+ */
+ clearTextInput() {
+ console.debug('Should clear input', this.el);
+ this.value = '';
+ }
+
+
+ render() {
+ const themedClasses = createThemedClasses(this.mode, this.color, 'text-input');
+ // TODO aria-labelledby={this.item.labelId}
+
+ // OLD RENDER
+ // '' +
+ // '' +
+ // '' +
+ // '