diff --git a/trac/ticket/api.py b/trac/ticket/api.py
--- a/trac/ticket/api.py
+++ b/trac/ticket/api.py
@@ -264,6 +264,7 @@
                 'type': config.get(name),
                 'order': config.getint(name + '.order', 0),
                 'label': config.get(name + '.label') or name.capitalize(),
+                'hint': config.get(name + '.hint') or None,
                 'value': config.get(name + '.value', '')
             }
             if field['type'] == 'select' or field['type'] == 'radio':
diff --git a/trac/ticket/templates/ticket.html b/trac/ticket/templates/ticket.html
--- a/trac/ticket/templates/ticket.html
+++ b/trac/ticket/templates/ticket.html
@@ -303,13 +303,13 @@
               <py:for each="idx, field in enumerate(row)"
                       py:with="value = ticket.get_value_or_default(field.name)">
                 <th class="col${idx + 1}" py:if="idx == 0 or not fullrow">
-                  <label for="field-${field.name}" py:if="field"
-                         py:strip="field.type == 'radio'">${field.edit_label or field.label or field.name}:</label>
+                  <label for="${field.type != 'radio' and ('field-' + field.name) or None}"
+                         py:if="field">${field.edit_label or field.label or field.name}:</label>
                 </th>
                 <td class="col${idx + 1}" py:if="idx == 0 or not fullrow"
                     colspan="${fullrow and 3 or None}">
                   <py:choose test="field.type" py:if="field">
-                    <select py:when="'select'" id="field-${field.name}" name="field_${field.name}">
+                    <select py:when="'select'" id="field-${field.name}" name="field_${field.name}" title="${field.hint}">
                       <option py:if="field.optional"></option>
                       <option py:for="option in field.options"
                               selected="${value == option or None}"
@@ -323,19 +323,20 @@
                       </optgroup>
                     </select>
                     <textarea py:when="'textarea'" id="field-${field.name}" name="field_${field.name}"
-                              cols="${field.width}" rows="${field.height}"
+                              cols="${field.width}" rows="${field.height}" title="${field.hint}"
                               py:content="value"></textarea>
                     <span py:when="'checkbox'">
                       <input type="checkbox" id="field-${field.name}" name="field_${field.name}"
-                             checked="${value == '1' and 'checked' or None}" value="1" />
+                             checked="${value == '1' and 'checked' or None}" value="1" title="${field.hint}" />
                       <input type="hidden" name="field_checkbox_${field.name}" value="1" />
                     </span>
-                    <label py:when="'radio'"
-                           py:for="idx, option in enumerate(field.options)">
-                      <input type="radio" name="field_${field.name}" value="${option}"
-                             checked="${value == option or None}" />
-                      ${option}
-                    </label>
+                    <span py:when="'radio'" title="${field.hint}">
+                      <label py:for="option in field.options">
+                        <input type="radio" name="field_${field.name}" value="${option}"
+                               checked="${value == option or None}" />
+                        ${option}
+                      </label>
+                    </span>
                     <py:otherwise><!--! Text input fields -->
                       <py:choose>
                         <span py:when="field.cc_entry"><!--! Special case for Cc: field -->
@@ -352,7 +353,7 @@
                         </span>
                         <!--! All the other text input fields -->
                         <input py:otherwise="" type="text" id="field-${field.name}"
-                          name="field_${field.name}" value="${value}" />
+                          name="field_${field.name}" value="${value}" title="${field.hint}" />
                       </py:choose>
                     </py:otherwise>
                   </py:choose>
diff --git a/trac/ticket/tests/api.py b/trac/ticket/tests/api.py
--- a/trac/ticket/tests/api.py
+++ b/trac/ticket/tests/api.py
@@ -24,9 +24,10 @@
         self.env.config.set('ticket-custom', 'test', 'text')
         self.env.config.set('ticket-custom', 'test.label', 'Test')
         self.env.config.set('ticket-custom', 'test.value', 'Foo bar')
+        self.env.config.set('ticket-custom', 'test.hint', 'An explanation')
         fields = TicketSystem(self.env).get_custom_fields()
         self.assertEqual({'name': 'test', 'type': 'text', 'label': 'Test',
-                          'value': 'Foo bar', 'order': 0},
+                          'value': 'Foo bar', 'order': 0, 'hint': 'An explanation'},
                          fields[0])
 
     def test_custom_field_select(self):
@@ -34,10 +35,11 @@
         self.env.config.set('ticket-custom', 'test.label', 'Test')
         self.env.config.set('ticket-custom', 'test.value', '1')
         self.env.config.set('ticket-custom', 'test.options', 'option1|option2')
+        self.env.config.set('ticket-custom', 'test.hint', 'An explanation')
         fields = TicketSystem(self.env).get_custom_fields()
         self.assertEqual({'name': 'test', 'type': 'select', 'label': 'Test',
                           'value': '1', 'options': ['option1', 'option2'],
-                          'order': 0},
+                          'order': 0, 'hint': 'An explanation'},
                          fields[0])
 
     def test_custom_field_optional_select(self):
@@ -48,7 +50,7 @@
         fields = TicketSystem(self.env).get_custom_fields()
         self.assertEqual({'name': 'test', 'type': 'select', 'label': 'Test',
                           'value': '1', 'options': ['option1', 'option2'],
-                          'order': 0, 'optional': True},
+                          'order': 0, 'optional': True, 'hint': None},
                          fields[0])
 
     def test_custom_field_textarea(self):
@@ -57,10 +59,11 @@
         self.env.config.set('ticket-custom', 'test.value', 'Foo bar')
         self.env.config.set('ticket-custom', 'test.cols', '60')
         self.env.config.set('ticket-custom', 'test.rows', '4')
+        self.env.config.set('ticket-custom', 'test.hint', 'An explanation')
         fields = TicketSystem(self.env).get_custom_fields()
         self.assertEqual({'name': 'test', 'type': 'textarea', 'label': 'Test',
                           'value': 'Foo bar', 'width': 60, 'height': 4,
-                          'order': 0},
+                          'order': 0, 'hint': 'An explanation'},
                          fields[0])
 
     def test_custom_field_order(self):

