diff --git a/django/forms/fields.py b/django/forms/fields.py
index 113a5aa..f1328a7 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -24,8 +24,8 @@ from django.utils.ipv6 import clean_ipv6_address
 from django.core.validators import EMPTY_VALUES
 
 from util import ErrorList
-from widgets import (TextInput, PasswordInput, HiddenInput,
-    MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select,
+from widgets import (TextInput, IntegerInput, EmailInput, URLInput, PasswordInput,
+    HiddenInput, MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select,
     NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput,
     SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION)
 
@@ -194,11 +194,14 @@ class CharField(Field):
         return smart_unicode(value)
 
     def widget_attrs(self, widget):
-        if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
+        attrs = super(CharField, self).widget_attrs(widget)
+        if self.max_length is not None and isinstance(widget, TextInput):
             # The HTML attribute is maxlength, not max_length.
-            return {'maxlength': str(self.max_length)}
+            attrs['maxlength'] = self.max_length
+        return attrs
 
 class IntegerField(Field):
+    widget = IntegerInput
     default_error_messages = {
         'invalid': _(u'Enter a whole number.'),
         'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
@@ -230,6 +233,14 @@ class IntegerField(Field):
             raise ValidationError(self.error_messages['invalid'])
         return value
 
+    def widget_attrs(self, widget):
+        attrs = super(IntegerField, self).widget_attrs(widget)
+        if self.min_value:
+            attrs['min'] = self.min_value
+        if self.max_value:
+            attrs['max'] = self.max_value
+        return attrs
+
 class FloatField(IntegerField):
     default_error_messages = {
         'invalid': _(u'Enter a number.'),
@@ -251,25 +262,22 @@ class FloatField(IntegerField):
             raise ValidationError(self.error_messages['invalid'])
         return value
 
-class DecimalField(Field):
+    def widget_attrs(self, widget):
+        attrs = super(FloatField, self).widget_attrs(widget)
+        attrs['step'] = 'any'
+        return attrs
+
+class DecimalField(IntegerField):
     default_error_messages = {
         'invalid': _(u'Enter a number.'),
-        'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
-        'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'),
         'max_digits': _('Ensure that there are no more than %s digits in total.'),
         'max_decimal_places': _('Ensure that there are no more than %s decimal places.'),
         'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
     }
 
     def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
-        self.max_value, self.min_value = max_value, min_value
         self.max_digits, self.decimal_places = max_digits, decimal_places
-        Field.__init__(self, *args, **kwargs)
-
-        if max_value is not None:
-            self.validators.append(validators.MaxValueValidator(max_value))
-        if min_value is not None:
-            self.validators.append(validators.MinValueValidator(min_value))
+        super(DecimalField, self).__init__(max_value, min_value, *args, **kwargs)
 
     def to_python(self, value):
         """
@@ -318,6 +326,14 @@ class DecimalField(Field):
             raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
         return value
 
+    def widget_attrs(self, widget):
+        attrs = super(DecimalField, self).widget_attrs(widget)
+        if self.max_digits:
+            attrs['maxlength'] = self.max_digits + 1 # for the dot
+        if self.decimal_places:
+            attrs['step'] = '0.%s1' % ('0' * (self.decimal_places-1))
+        return attrs
+
 class BaseTemporalField(Field):
 
     def __init__(self, input_formats=None, *args, **kwargs):
@@ -445,6 +461,7 @@ class RegexField(CharField):
         self.validators.append(validators.RegexValidator(regex=regex))
 
 class EmailField(CharField):
+    widget = EmailInput
     default_error_messages = {
         'invalid': _(u'Enter a valid e-mail address.'),
     }
@@ -571,6 +588,7 @@ class ImageField(FileField):
         return f
 
 class URLField(CharField):
+    widget = URLInput
     default_error_messages = {
         'invalid': _(u'Enter a valid URL.'),
         'invalid_link': _(u'This URL appears to be a broken link.'),
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 9b95c31..b9f405b 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -17,12 +17,11 @@ from django.utils.safestring import mark_safe
 from django.utils import datetime_safe, formats
 
 __all__ = (
-    'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
-    'HiddenInput', 'MultipleHiddenInput', 'ClearableFileInput',
-    'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
-    'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
-    'CheckboxSelectMultiple', 'MultiWidget',
-    'SplitDateTimeWidget',
+    'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'EmailInput', 'URLInput',
+    'IntegerInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
+    'ClearableFileInput', 'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput',
+    'Textarea', 'CheckboxInput', 'Select', 'NullBooleanSelect', 'SelectMultiple',
+    'RadioSelect', 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
 )
 
 MEDIA_TYPES = ('css','js')
@@ -233,7 +232,16 @@ class Input(Widget):
 class TextInput(Input):
     input_type = 'text'
 
-class PasswordInput(Input):
+class IntegerInput(TextInput):
+    input_type = 'number'
+
+class EmailInput(TextInput):
+    input_type = 'email'
+
+class URLInput(TextInput):
+    input_type = 'url'
+
+class PasswordInput(TextInput):
     input_type = 'password'
 
     def __init__(self, attrs=None, render_value=False):
@@ -369,9 +377,7 @@ class Textarea(Widget):
         return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs),
                 conditional_escape(force_unicode(value))))
 
-class DateInput(Input):
-    input_type = 'text'
-
+class DateInput(TextInput):
     def __init__(self, attrs=None, format=None):
         super(DateInput, self).__init__(attrs)
         if format:
@@ -400,9 +406,7 @@ class DateInput(Input):
             pass
         return super(DateInput, self)._has_changed(self._format_value(initial), data)
 
-class DateTimeInput(Input):
-    input_type = 'text'
-
+class DateTimeInput(TextInput):
     def __init__(self, attrs=None, format=None):
         super(DateTimeInput, self).__init__(attrs)
         if format:
@@ -431,9 +435,7 @@ class DateTimeInput(Input):
             pass
         return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
 
-class TimeInput(Input):
-    input_type = 'text'
-
+class TimeInput(TextInput):
     def __init__(self, attrs=None, format=None):
         super(TimeInput, self).__init__(attrs)
         if format:
