diff --git a/.changeset/famous-foxes-joke.md b/.changeset/famous-foxes-joke.md
new file mode 100644
index 000000000..e9bd9bfea
--- /dev/null
+++ b/.changeset/famous-foxes-joke.md
@@ -0,0 +1,6 @@
+---
+'@crowdstrike/ember-toucan-core': minor
+---
+
+- Added label and hint named blocks to all the Field components.
+- Removed Fieldset.
diff --git a/docs/components/checkbox-field/index.md b/docs/components/checkbox-field/index.md
index cae1c4b24..034915a87 100644
--- a/docs/components/checkbox-field/index.md
+++ b/docs/components/checkbox-field/index.md
@@ -4,15 +4,71 @@ Provides an underlying checkbox element building on top of the Field component.
 
 ## Label
 
-Provide a string to `@label` to render the text into the `<label>` of the Field.
+Required.
+
+Use either the `@label` component argument or the `:label` named block.
+
+Provide a string to the `@label` component argument or content to the `:label` named block to render into the Label section.
+
+### @label
+
+```hbs template
+<Form::Fields::Checkbox
+  @label='Label here'
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :label
+
+```hbs template
+<Form::Fields::Checkbox
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+>
+  <:label>Here is a label <IconButton><Tooltip /><IconButton></:label>
+</Form::Fields::Checkbox>
+```
 
 ## Hint
 
-Optional. Provide a string to `@hint` to render the text into the Hint section of the Field.
+Optional.
+
+Use either the `@hint` component argument or the `:hint` named block.
+
+Provide a string to the `@hint` component argument or content to `:hint` named block to render into the Hint section.
+
+### @hint
+
+```hbs template
+<Form::Fields::Checkbox
+  @hint='Here is a hint'
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :hint
+
+```hbs template
+<Form::Fields::Checkbox
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+>
+  <:hint>Here is a hint <Link to='somewhere'>Link</Link></:hint>
+</Form::Fields::Checkbox>
+```
 
 ## Error
 
-Optional. Provide a string or array of strings to `@error` to render the text into the Error section of the Field.
+Optional.
+
+Provide a string or array of strings to `@error` to render the text into the Error section of the Field.
 
 ```hbs
 <Form::Fields::Checkbox @label='Single error' @error='Error' />
@@ -139,6 +195,16 @@ Target the error block via `data-error`.
   />
 </div>
 
+### CheckboxField with label and hint blocks
+
+<div class='mb-4 w-64'>
+  <Form::Fields::Checkbox
+  >
+    <:label>Label <svg class="inline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 3a9 9 0 11-6.364 2.636A8.972 8.972 0 0112 3zm0 4.7v5.2m0 3.39v.01" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg></:label>
+    <:hint>Select <a href="https://www.crowdstrike.com/">link</a></:hint>
+  </Form::Fields::Checkbox>
+</div>
+
 ### CheckboxField with label and isDisabled
 
 <div class='mb-4 w-64'>
diff --git a/docs/components/checkbox-group-field/index.md b/docs/components/checkbox-group-field/index.md
index 11a91d7d1..e2085397b 100644
--- a/docs/components/checkbox-group-field/index.md
+++ b/docs/components/checkbox-group-field/index.md
@@ -8,11 +8,73 @@ Provides a checkbox group to be used within forms. It yields [CheckboxFields](./
 
 ## Label
 
-Provide a string to `@label` to render the text into the `<legend>` of the fieldset.
+Required.
+
+Use either the `@label` component argument or the `:label` named block.
+
+Provide a string to the `@label` component argument or content to the `:label` named block to render into the legend of the fieldset.
+
+### @label
+
+```hbs template
+<Form::Fields::CheckboxGroup
+  @label='Label here'
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :label
+
+```hbs template
+<Form::Fields::CheckboxGroup
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+>
+  {{!-- default block is required here when using :label --}}
+  <:default as |group|>
+    {{!-- render checkboxes here --}}
+  </:default>
+  <:label>Here is a label <IconButton><Tooltip /><IconButton></:label>
+</Form::Fields::CheckboxGroup>
+```
 
 ## Hint
 
-Optional. Provide a string to `@hint` to render the text into the Hint section of the fieldset.
+Optional.
+
+Use either the `@hint` component argument or the `:hint` named block.
+
+Provide a string to the `@hint` component argument or content to `:hint` named block to render into the Hint section.
+
+### @hint
+
+```hbs template
+<Form::Fields::CheckboxGroup
+  @hint='Here is a hint'
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :hint
+
+```hbs template
+<Form::Fields::CheckboxGroup
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+>
+  {{! default block is required here when using :hint}}
+  <:default as |group|>
+    {{! render checkboxes here }}
+  </:default>
+  <:hint>Here is a hint <Link to='somewhere'>Link</Link></:hint>
+</Form::Fields::CheckboxGroup>
+```
 
 ## Error
 
@@ -126,6 +188,20 @@ Consumers have direct access to the underlying [checkbox element](https://develo
   </Form::Fields::CheckboxGroup>
 </div>
 
+### CheckboxGroupField with label block and hint block
+
+<div class='mb-4 w-64'>
+  <Form::Fields::CheckboxGroup @name='options-b'>
+    <:default as |group|>
+      <group.CheckboxField @label='Option 1' @value='option-1' />
+      <group.CheckboxField @label='Option 2' @value='option-2' />
+      <group.CheckboxField @label='Option 3' @value='option-3' />
+    </:default>
+    <:label>Label <svg class="inline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 3a9 9 0 11-6.364 2.636A8.972 8.972 0 0112 3zm0 4.7v5.2m0 3.39v.01" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg></:label>
+    <:hint>Select an option <a href="https://www.crowdstrike.com/">link</a></:hint>
+  </Form::Fields::CheckboxGroup>
+</div>
+
 ### CheckboxGroupField with label and error
 
 <div class='mb-4 w-64'>
diff --git a/docs/components/fieldset/demo/base-demo.md b/docs/components/fieldset/demo/base-demo.md
deleted file mode 100644
index 3930d32d4..000000000
--- a/docs/components/fieldset/demo/base-demo.md
+++ /dev/null
@@ -1,16 +0,0 @@
-```hbs template
-<Form::Fieldset
-  @label='Label'
-  @hint='Extra information about the fieldset'
-  @error='Error message'
->
-  <p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render
-    here!~</p>
-</Form::Fieldset>
-```
-
-```js component
-import Component from '@glimmer/component';
-
-export default class extends Component {}
-```
diff --git a/docs/components/fieldset/index.md b/docs/components/fieldset/index.md
deleted file mode 100644
index 1efd66a09..000000000
--- a/docs/components/fieldset/index.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# Fieldset
-
-Fieldset is a component to aid in creating form components that require an underlying `<fieldset>` and `<legend>`. It is similar to Field, in that it provides an opinionated shell for building other components such as checkbox groups and radio groups.
-
-## Label
-
-Provide a string to `@label` to render the text into the `<legend>` of the Fieldset. This is required.
-
-```hbs
-<Form::Fieldset @label='Label' />
-```
-
-## Hint
-
-Optional. Provide a string to `@hint` to render the text into the Hint section of the Fieldset.
-
-```hbs
-<Form::Fieldset @label='Label' @hint='Hint' />
-```
-
-## Error
-
-Optional. Provide a string or array of strings to `@error` to render the text into the Error section of the Fieldset.
-
-```hbs
-<Form::Fieldset @label='Label' @error='Error' />
-```
-
-```hbs
-<Form::Fieldset @label='Label' @error={{(array 'Error 1' 'Error 2')}} />
-```
-
-## Disabled State
-
-Set the `@isDisabled` argument to disable the fieldset. When disabled, all form controls that are descendants of the fieldset, are disabled, meaning they are not editable and won't be submitted along with the form. Learn more via the [fieldset documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset#attributes).
-
-```hbs
-<Form::Fieldset @label='Label' @isDisabled={{true}}>
-  <!-- This is now disabled as well -->
-  <input />
-</Form::Fieldset>
-```
-
-## Attributes and Modifiers
-
-Consumers have direct access to the underlying [fieldset element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset), so all attributes are supported.
-
-```hbs
-<Form::Fieldset @label='Label' name='my-checkboxes' data-fieldset />
-```
-
-## Test Selectors
-
-### Root Element
-
-The wrapping element is a `<fieldset>` and attributes are spread directly on it as mentioned above. Due to that, one can target the fieldset with any data attribute.
-
-```hbs
-<Form::Fieldset @label='Label' data-fieldset />
-```
-
-### Label
-
-Target the label element via `data-label`.
-
-### Hint
-
-Target the hint block via `data-hint`.
-
-### Wrapping Content Container
-
-The `yield` is wrapped in a div container that can be targeted with `data-control`.
-
-### Error
-
-Target the error block via `data-error`.
-
-## All UI States
-
-<div class="flex flex-col space-y-4" style="max-width: 14rem">
-<Form::Fieldset @label='Label'>
-
-  <p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p>
-</Form::Fieldset>
-
-<Form::Fieldset @label='Label' @hint="With hint text">
-
-  <p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p>
-</Form::Fieldset>
-
-<Form::Fieldset @label='Label' @error="With error">
-
-  <p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p>
-</Form::Fieldset>
-
-<Form::Fieldset @label='Label' @hint="With hint text" @error="With error">
-
-  <p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p>
-</Form::Fieldset>
-
-<Form::Fieldset @label='Label' @hint="With hint text" @error={{(array "With error 1" "With error 2" "With error 3")}}>
-
-  <p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p>
-</Form::Fieldset>
-</div>
diff --git a/docs/components/file-input-field/index.md b/docs/components/file-input-field/index.md
index 50d34e292..164843ab0 100644
--- a/docs/components/file-input-field/index.md
+++ b/docs/components/file-input-field/index.md
@@ -5,36 +5,100 @@ Provides an underlying `<input type="file">` element building on top of the Fiel
 ## Accepts
 
 Optional.
+
 Used to specify the types of files allowed. [See related documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept).
 
 ## Label
 
 Required.
-Provide a string to `@label` to render the text into the `<input type="file">` of the Field.
+
+Use either the `@label` component argument or the `:label` named block.
+
+Provide a string to the `@label` component argument or content to the `:label` named block to render into the Label section of the Field.
+
+### @label
+
+```hbs
+<Form::Fields::FileInput
+  @deleteLabel='Delete file'
+  @label='Label'
+  @files={{this.files}}
+  @trigger='Browse Files'
+  @onChange={{this.handleChange}}
+/>
+```
+
+### :label
+
+```hbs
+<Form::Fields::FileInput
+  @deleteLabel='Delete file'
+  @files={{this.files}}
+  @hint='Hint text'
+  @trigger='Browse Files'
+  @onChange={{this.handleChange}}
+>
+  <:label>Here is a label <IconButton><Tooltip /><IconButton></:label>
+</Form::Fields::FileInput>
+```
 
 ## Delete Label
 
 Required.
+
 Provide a string to `@deleteLabel` to render the accessible text (screenread-only) into the delete button.
 
 ## Trigger
 
 Required.
+
 Provide trigger text for the FileInputField (aka `Select Files`) via `@trigger`.
 
 ## Hint
 
 Optional.
-Provide a string to `@hint` to render the text into the Hint section of the Field.
+
+Use either the `@hint` component argument or the `:hint` named block.
+
+Provide a string to the `@hint` component argument or content to `:hint` named block to render into the Hint section of the Field.
+
+### @hint
+
+```hbs
+<Form::Fields::FileInput
+  @hint='Here is a hint'
+  @deleteLabel='Delete file'
+  @label='Label'
+  @files={{this.files}}
+  @trigger='Browse Files'
+  @onChange={{this.handleChange}}
+/>
+```
+
+### :hint
+
+```hbs
+<Form::Fields::FileInput
+  @label='Label'
+  @deleteLabel='Delete file'
+  @files={{this.files}}
+  @trigger='Browse Files'
+  @onChange={{this.handleChange}}
+>
+  <:hint>Here is a hint <Link to='somewhere'>Link</Link></:hint>
+</Form::Fields::FileInput>
+```
 
 ## Error
 
 Optional.
+
 Provide a string to `@error` to render the text into the Error section of the Field.
 
 ## Files
 
 Optional.
+
 Provide an array of [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects to the File input via `@files`.
 
 **Note:** To make things easier, the `@files` argument is an array. This makes it convenient to use existing Array methods like `find` and `filter`.
@@ -42,11 +106,13 @@ Provide an array of [File](https://developer.mozilla.org/en-US/docs/Web/API/File
 ## Multiple
 
 Optional.
+
 If true, allows users to upload multiple files. If false, the `multiple` attribute is not added to the underlying input.
 
 ## Value and onChange
 
 Optional
+
 To tie into the input event, provide `@onChange`. `@onChange` will return two arguments:
 
 1. the list of files selected
@@ -152,32 +218,58 @@ Target the trash icon button via `data-delete-file`.
 
 ## UI States
 
-<div class='flex flex-col gap-y-5'>
+## FileInputField with label
+
+<div class='mb-4 w-64'>
   <Form::Fields::FileInput
     @deleteLabel='Delete file'
     @label='Label'
     @trigger='Browse Files'
   />
+</div>
 
-<Form::Fields::FileInput
-@deleteLabel='Delete file'
-@label='Label'
-@hint='Hint text'
-@trigger='Browse Files'
-/>
+## FileInputField with label and hint
 
-<Form::Fields::FileInput
-@deleteLabel='Delete file'
-@label='Label'
-@error='Here is an error'
-@trigger='Browse Files'
-/>
+<div class='mb-4 w-64'>
+  <Form::Fields::FileInput
+    @deleteLabel='Delete file'
+    @label='Label'
+    @hint='Hint text'
+    @trigger='Browse Files'
+    />
+</div>
 
-<Form::Fields::FileInput
-@deleteLabel='Delete file'
-@label='Label'
-@isDisabled={{true}}
-@trigger='Browse Files'
-/>
+## FileInputField with label and hint blocks
+
+<div class='mb-4 w-64'>
+  <Form::Fields::FileInput
+    @deleteLabel='Delete file'
+    @trigger='Browse Files'
+  >
+  <:label>Label <svg class="inline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 3a9 9 0 11-6.364 2.636A8.972 8.972 0 0112 3zm0 4.7v5.2m0 3.39v.01" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg></:label>
+  <:hint>Hint text <a href="https://www.crowdstrike.com/">link</a></:hint>
+  </Form::Fields::FileInput>
+</div>
 
+## FileInputField with label, hint and error
+
+<div class='mb-4 w-64'>
+  <Form::Fields::FileInput
+    @deleteLabel='Delete file'
+    @label='Label'
+    @hint='Hint'
+    @error='Here is an error'
+    @trigger='Select Files'
+  />
+</div>
+  
+## FileInputField with label and isDisabled
+
+<div class='mb-4 w-64'>
+  <Form::Fields::FileInput
+    @deleteLabel='Delete file'
+    @label='Label'
+    @isDisabled={{true}}
+    @trigger='Browse Files'
+    />
 </div>
diff --git a/docs/components/input-field/index.md b/docs/components/input-field/index.md
index e80cd76b0..c35569fb7 100644
--- a/docs/components/input-field/index.md
+++ b/docs/components/input-field/index.md
@@ -4,19 +4,77 @@ Provides an underlying `<input>` element building on top of the Field component.
 
 ## Label
 
-Provide a string to `@label` to render the text into the `<label>` of the Field.
+Required.
+
+Use either the `@label` component argument or the `:label` named block.
+
+Provide a string to the `@label` component argument or content to the `:label` named block to render into the Label section of the Field.
+
+### @label
+
+```hbs
+<Form::Fields::Input
+  @label='Label'
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+  type='text'
+/>
+```
+
+### :label
+
+```hbs
+<Form::Fields::Input
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+  type='text'
+>
+  <:label>Here is a label <IconButton><Tooltip /><IconButton></:label>
+</Form::Fields::Input>
+```
 
 ## Hint
 
-Optional. Provide a string to `@hint` to render the text into the Hint section of the Field.
+Optional.
 
-## Error
+Use either the `@hint` component argument or the `:hint` named block.
+
+Provide a string to the `@hint` component argument or content to `:hint` named block to render into the Hint section of the Field.
+
+### @hint
 
-Optional. Provide a string or array of strings to `@error` to render the text into the Error section of the Field.
+```hbs
+<Form::Fields::Input
+  @label='Label'
+  @hint='Type "input" into the field'
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+  type='text'
+/>
+```
+
+### :hint
+
+```hbs
+<Form::Fields::Input
+  @label='Label'
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+  type='text'
+>
+  <:hint>Here is a hint <Link to='somewhere'>Link</Link></:hint>
+</Form::Fields::Input>
+```
 
 ## Error
 
-Optional. Provide a string or array of strings to `@error` to render the text into the Error section of the Field.
+Optional.
+
+Provide a string or array of strings to `@error` to render the text into the Error section of the Field.
 
 ```hbs
 <Form::Fields::Input @label='Single error' @error='Error' />
@@ -110,6 +168,16 @@ Target the error block via `data-error`.
   />
 </div>
 
+### InputField with label and hint blocks
+
+<div class='mb-4 w-64'>
+  <Form::Fields::Input
+  >
+  <:label>Label <svg class="inline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 3a9 9 0 11-6.364 2.636A8.972 8.972 0 0112 3zm0 4.7v5.2m0 3.39v.01" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg></:label>
+  <:hint>Hint text <a href="https://www.crowdstrike.com/">link</a></:hint>
+  </Form::Fields::Input>
+</div>
+
 ### InputField with label and error
 
 <div class='mb-4 w-64'>
diff --git a/docs/components/input/index.md b/docs/components/input/index.md
index 04981d2bd..a221418d3 100644
--- a/docs/components/input/index.md
+++ b/docs/components/input/index.md
@@ -1,4 +1,4 @@
-# Input 
+# Input
 
 Provides a Toucan-styled [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). If you are building forms, you may be interested in the InputField component instead.
 
@@ -15,10 +15,7 @@ To set the `value` attribute of the `<input>`, provide `@value`.
 To tie into the input event, provide `@onChange`. `@onChange` will return two arguments, the first being the value, while the second being the raw event.
 
 ```hbs
-<Form::Controls::Input
-  @value={{this.value}}
-  @onChange={{this.handleChange}}
-/>
+<Form::Controls::Input @value={{this.value}} @onChange={{this.handleChange}} />
 ```
 
 ```js
@@ -40,4 +37,5 @@ export default class extends Component {
 Set the `@isDisabled` argument to disable the `<input>`.
 
 ## Error State
+
 Set the `@hasError` argument to apply an error box shadow to the `<input>`.
diff --git a/docs/components/radio-field/demo/base-demo.md b/docs/components/radio-field/demo/base-demo.md
index ee64cc682..1368338ef 100644
--- a/docs/components/radio-field/demo/base-demo.md
+++ b/docs/components/radio-field/demo/base-demo.md
@@ -9,6 +9,7 @@
   />
   <Form::Fields::Radio
     @label='Option 2'
+    @hint='Some hint'
     @name='options'
     @value='option-2'
     @onChange={{this.updateValue}}
diff --git a/docs/components/radio-field/index.md b/docs/components/radio-field/index.md
index 478e728f7..1582045bf 100644
--- a/docs/components/radio-field/index.md
+++ b/docs/components/radio-field/index.md
@@ -4,19 +4,85 @@ Provides an opinionated radio element building on top of the Field component. If
 
 ## Label
 
-Provide a string to `@label` to render the text into the `<label>` of the Field. This is required.
+Required.
+
+Use either the `@label` component argument or the `:label` named block.
+
+Provide a string to the `@label` component argument or content to the `:label` named block to render into the Label section of the Field.
+
+### @label
+
+```hbs
+<Form::Fields::Radio
+  @label='Label'
+  @hint='Some hint'
+  @name='options'
+  @value='option-2'
+  @onChange={{this.updateValue}}
+  @selectedValue='option-2'
+/>
+```
+
+### :label
+
+```hbs
+<Form::Fields::Radio
+  @hint='Some hint'
+  @name='options'
+  @value='option-2'
+  @onChange={{this.updateValue}}
+  @selectedValue='option-2'
+>
+    <:label>Here is a label <IconButton><Tooltip /><IconButton></:label>
+</Form::Fields::Radio>
+```
 
 ## Name
 
-Provide a string to `@name` to set the `name` attribute of the Field. This is required.
+Required.
+
+Provide a string to `@name` to set the `name` attribute of the Field.
 
 ## Value
 
-Provide a string to `@value` to set the `value` attribute of the radio. This is required.
+Required.
+
+Provide a string to `@value` to set the `value` attribute of the radio.
 
 ## Hint
 
-Optional. Provide a string to `@hint` to render the text into the Hint section of the Field.
+Optional.
+
+Use either the `@hint` component argument or the `:hint` named block.
+
+Provide a string to the `@hint` component argument or content to `:hint` named block to render into the Hint section of the Field.
+
+### @hint
+
+```hbs
+<Form::Fields::Radio
+  @label='Label'
+  @hint='Some hint'
+  @name='options'
+  @value='option-2'
+  @onChange={{this.updateValue}}
+  @selectedValue='option-2'
+/>
+```
+
+### :hint
+
+```hbs
+<Form::Fields::Radio
+  @label='Label'
+  @name='options'
+  @value='option-2'
+  @onChange={{this.updateValue}}
+  @selectedValue='option-2'
+>
+  <:hint>Here is a hint <Link to='somewhere'>Link</Link></:hint>
+</Form::Fields::Radio>
+```
 
 ## Value, Checked State, and onChange
 
@@ -109,6 +175,18 @@ Target the hint block via `data-hint`.
   />
 </div>
 
+### RadioField with label and hint blocks
+
+<div class='mb-4 w-64'>
+  <Form::Fields::Radio
+    @name='options-b'
+    @value='option-1'
+  >
+  <:label>Label <svg class="inline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 3a9 9 0 11-6.364 2.636A8.972 8.972 0 0112 3zm0 4.7v5.2m0 3.39v.01" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg></:label>
+  <:hint>Hint text <a href="https://www.crowdstrike.com/">link</a></:hint>
+  </Form::Fields::Radio>
+</div>
+
 ### RadioField with label and isDisabled
 
 <div class='mb-4 w-64'>
diff --git a/docs/components/radio-group-field/index.md b/docs/components/radio-group-field/index.md
index c68006478..3b33b1dad 100644
--- a/docs/components/radio-group-field/index.md
+++ b/docs/components/radio-group-field/index.md
@@ -8,15 +8,81 @@ Provides a radio group to be used within forms. It yields [RadioFields](./radio-
 
 ## Label
 
-Provide a string to `@label` to render the text into the `<legend>` of the fieldset.
+Required.
+
+Use either the `@label` component argument or the `:label` named block.
+
+Provide a string to the `@label` component argument or content to the `:label` named block to render into the legend of the fieldset.
+
+### @label
+
+```hbs
+<Form::Fields::RadioGroup
+  @label='Label'
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :label
+
+```hbs
+<Form::Fields::RadioGroup
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+>
+  {{!-- note default block is required here --}}
+  <:default as |group|>
+    {{!-- radio components rendered here --}}
+  </:default>
+  <:label>Here is a label <IconButton><Tooltip /><IconButton></:label>
+</Form::Fields::RadioGroup>
+```
 
 ## Hint
 
-Optional. Provide a string to `@hint` to render the text into the Hint section of the fieldset.
+Optional.
+
+Use either the `@hint` component argument or the `:hint` named block.
+
+Provide a string to the `@hint` component argument or content to `:hint` named block to render into the legend of the fieldset.
+
+### @hint
+
+```hbs
+<Form::Fields::RadioGroup
+  @label='Label'
+  @hint='Extra information about the field'
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :hint
+
+```hbs
+<Form::Fields::RadioGroup
+  @label='Label'
+  @name='options'
+  @value={{this.groupValue}}
+  @onChange={{this.updateValue}}
+>
+  {{! note default block is required here }}
+  <:default as |group|>
+    {{! radio components rendered here }}
+  </:default>
+  <:hint>Here is a hint <Link to='somewhere'>Link</Link></:hint>
+</Form::Fields::RadioGroup>
+```
 
 ## Error
 
-Optional. Provide a string or array of strings to `@error` to render the text into the Error section of the fieldset.
+Optional.
+
+Provide a string or array of strings to `@error` to render the text into the Error section of the fieldset.
 
 ```hbs
 <Form::Fields::RadioGroup @label='Label' @name='single-error' @error='Error' />
@@ -134,6 +200,20 @@ Consumers have direct access to the underlying [radio element](https://developer
   </Form::Fields::RadioGroup>
 </div>
 
+### RadioGroupField with label and hint blocks
+
+<div class='mb-4 w-64'>
+  <Form::Fields::RadioGroup @name='options-b'>
+    <:default as |group|>
+      <group.RadioField @label='Option 1' @value='option-1' />
+      <group.RadioField @label='Option 2' @value='option-2' />
+      <group.RadioField @label='Option 3' @value='option-3' />
+    </:default>
+  <:label>Label <svg class="inline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 3a9 9 0 11-6.364 2.636A8.972 8.972 0 0112 3zm0 4.7v5.2m0 3.39v.01" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg></:label>
+  <:hint>Select an option <a href="https://www.crowdstrike.com/">link</a></:hint>
+  </Form::Fields::RadioGroup>
+</div>
+
 ### RadioGroupField with label and error
 
 <div class='mb-4 w-64'>
diff --git a/docs/components/textarea-field/index.md b/docs/components/textarea-field/index.md
index cec08e3f6..d8c6b9480 100644
--- a/docs/components/textarea-field/index.md
+++ b/docs/components/textarea-field/index.md
@@ -4,15 +4,73 @@ Provides an underlying `<textarea>` element building on top of the Field compone
 
 ## Label
 
-Provide a string to `@label` to render the text into the `<label>` of the Field.
+Required.
+
+Use either the `@label` component argument or the `:label` named block.
+
+Provide a string to the `@label` component argument or content to the `:label` named block to render into the Label section of the Field.
+
+### @label
+
+```hbs
+<Form::Fields::Textarea
+  @label='Label'
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :label
+
+```hbs
+<Form::Fields::Textarea
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+>
+  <:label>Here is a label <IconButton><Tooltip /><IconButton></:label>
+</Form::Fields::Textarea>
+```
 
 ## Hint
 
-Optional. Provide a string to `@hint` to render the text into the Hint section of the Field.
+Optional.
+
+Use either the `@hint` component argument or the `:hint` named block.
+
+Provide a string to the `@hint` component argument or content to `:hint` named block to render into the Hint section of the Field.
+
+### @hint
+
+```hbs
+<Form::Fields::Textarea
+  @label='Label'
+  @hint='Here is a hint'
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+/>
+```
+
+### :hint
+
+```hbs
+<Form::Fields::Textarea
+  @label='Label'
+  @error={{this.errorMessage}}
+  @value={{this.value}}
+  @onChange={{this.updateValue}}
+>
+  <:hint>Here is a hint <Link to='somewhere'>Link</Link></:hint>
+</Form::Fields::Textarea>
+```
 
 ## Error
 
-Optional. Provide a string or array of strings to `@error` to render the text into the Error section of the Field.
+Optional.
+
+Provide a string or array of strings to `@error` to render the text into the Error section of the Field.
 
 ```hbs
 <Form::Fields::Textarea @label='Single error' @error='Error' />
@@ -131,6 +189,16 @@ Target the error block via `data-error`.
   />
 </div>
 
+### TextareaField with label and hint blocks
+
+<div class='mb-4 w-64'>
+  <Form::Fields::Textarea
+  >
+  <:label>Label <svg class="inline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 3a9 9 0 11-6.364 2.636A8.972 8.972 0 0112 3zm0 4.7v5.2m0 3.39v.01" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg></:label>
+  <:hint>Select an option <a href="https://www.crowdstrike.com/">link</a></:hint>
+  </Form::Fields::Textarea>
+</div>
+
 ### TextareaField with label and isDisabled
 
 <div class='mb-4 w-64'>
diff --git a/packages/ember-toucan-core/package.json b/packages/ember-toucan-core/package.json
index ee78dae0b..edcc13c44 100644
--- a/packages/ember-toucan-core/package.json
+++ b/packages/ember-toucan-core/package.json
@@ -128,7 +128,6 @@
       "./components/form/fields/radio-group.js": "./dist/_app_/components/form/fields/radio-group.js",
       "./components/form/fields/radio.js": "./dist/_app_/components/form/fields/radio.js",
       "./components/form/fields/textarea.js": "./dist/_app_/components/form/fields/textarea.js",
-      "./components/form/fieldset.js": "./dist/_app_/components/form/fieldset.js",
       "./components/form/file-input/delete-button.js": "./dist/_app_/components/form/file-input/delete-button.js",
       "./components/form/file-input/list-item.js": "./dist/_app_/components/form/file-input/list-item.js",
       "./components/form/file-input/list.js": "./dist/_app_/components/form/file-input/list.js"
diff --git a/packages/ember-toucan-core/src/-private/assert-block-or-argument-exists.ts b/packages/ember-toucan-core/src/-private/assert-block-or-argument-exists.ts
new file mode 100644
index 000000000..7cd13c9d1
--- /dev/null
+++ b/packages/ember-toucan-core/src/-private/assert-block-or-argument-exists.ts
@@ -0,0 +1,39 @@
+import { assert } from '@ember/debug';
+
+export type AssertBlockOrArg = {
+  blockExists: boolean;
+  argName: string;
+  arg?: string;
+  isRequired?: boolean;
+};
+
+/**
+ * @param {object} AssertBlockOrArg
+ *
+ **/
+const assertBlockOrArgumentExists = ({
+  blockExists,
+  argName,
+  arg,
+  isRequired,
+}: AssertBlockOrArg) => {
+  if (isRequired) {
+    assert(
+      `You need either :${argName} or @${argName}`,
+      Boolean(blockExists || arg)
+    );
+  }
+
+  assert(
+    `You can have :${argName} or @${argName}, but not both`,
+    !(blockExists && arg)
+  );
+
+  if (blockExists || arg) {
+    return true;
+  }
+
+  return false;
+};
+
+export default assertBlockOrArgumentExists;
diff --git a/packages/ember-toucan-core/src/components/form/fields/checkbox-group.hbs b/packages/ember-toucan-core/src/components/form/fields/checkbox-group.hbs
index 91e7571dd..f2453ea1f 100644
--- a/packages/ember-toucan-core/src/components/form/fields/checkbox-group.hbs
+++ b/packages/ember-toucan-core/src/components/form/fields/checkbox-group.hbs
@@ -1,20 +1,63 @@
-<Form::Fieldset
-  @label={{@label}}
-  @hint={{@hint}}
-  @error={{@error}}
-  @isDisabled={{@isDisabled}}
-  aria-invalid={{if @error "true"}}
-  ...attributes
->
-  {{yield
-    (hash
-      CheckboxField=(component
-        (ensure-safe-component this.CheckboxFieldComponent)
-        name=@name
-        onChange=this.handleInput
-        isDisabled=@isDisabled
-        selectedValues=@value
+<Form::Field as |field|>
+  <fieldset
+    aria-describedby="{{if @error field.errorId}} {{if @hint field.hintId}}"
+    disabled={{@isDisabled}}
+    {{! attribute aria-invalid is not supported by the element fieldset with the implicit role of group }}
+    ...attributes
+  >
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash
+          blockExists=(has-block "label")
+          argName="label"
+          arg=@label
+          isRequired=true
+        )
       )
-    )
-  }}
-</Form::Fieldset>
\ No newline at end of file
+    }}
+      <legend class="type-md-tight text-body-and-labels block" data-label>
+        {{#if (has-block "label")}}
+          {{yield to="label"}}
+        {{else}}
+          {{@label}}
+        {{/if}}
+      </legend>
+    {{/if}}
+
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash blockExists=(has-block "hint") argName="hint" arg=@hint)
+      )
+    }}
+      <field.Hint id={{field.hintId}} data-hint>
+        {{#if (has-block "hint")}}
+          {{yield to="hint"}}
+        {{else}}
+          {{@hint}}
+        {{/if}}
+      </field.Hint>
+    {{/if}}
+
+    <field.Control
+      class="mt-1.5 flex flex-col space-y-2 rounded-sm p-1
+        {{if @error 'shadow-error-outline'}}"
+      data-control
+    >
+      {{yield
+        (hash
+          CheckboxField=(component
+            (ensure-safe-component this.CheckboxFieldComponent)
+            name=@name
+            onChange=this.handleInput
+            isDisabled=@isDisabled
+            selectedValues=@value
+          )
+        )
+      }}
+    </field.Control>
+
+    {{#if @error}}
+      <field.Error id={{field.errorId}} @error={{@error}} data-error />
+    {{/if}}
+  </fieldset>
+</Form::Field>
\ No newline at end of file
diff --git a/packages/ember-toucan-core/src/components/form/fields/checkbox-group.ts b/packages/ember-toucan-core/src/components/form/fields/checkbox-group.ts
index 39b7618f7..7e5b0f614 100644
--- a/packages/ember-toucan-core/src/components/form/fields/checkbox-group.ts
+++ b/packages/ember-toucan-core/src/components/form/fields/checkbox-group.ts
@@ -1,8 +1,10 @@
 import Component from '@glimmer/component';
 import { action } from '@ember/object';
 
+import assertBlockOrArgumentExists from '../../../-private/assert-block-or-argument-exists';
 import CheckboxFieldComponent from './checkbox';
 
+import type { AssertBlockOrArg } from '../../../-private/assert-block-or-argument-exists';
 import type { WithBoundArgs } from '@glint/template';
 
 export interface ToucanFormCheckboxGroupFieldComponentSignature {
@@ -26,7 +28,7 @@ export interface ToucanFormCheckboxGroupFieldComponentSignature {
     /**
      * Provide a string to this argument to render inside of the label tag.
      */
-    label: string;
+    label?: string;
 
     /**
      * Sets the name attribute of the checkboxes. A string specifying a name for the input control. This name is submitted along with the control's value when the form data is submitted.
@@ -54,12 +56,22 @@ export interface ToucanFormCheckboxGroupFieldComponentSignature {
         >;
       }
     ];
+    label: [];
+    hint: [];
   };
 }
 
 export default class ToucanFormCheckboxGroupFieldComponent extends Component<ToucanFormCheckboxGroupFieldComponentSignature> {
   CheckboxFieldComponent = CheckboxFieldComponent;
 
+  assertBlockOrArgumentExists = ({
+    blockExists,
+    argName,
+    arg,
+    isRequired,
+  }: AssertBlockOrArg) =>
+    assertBlockOrArgumentExists({ blockExists, argName, arg, isRequired });
+
   @action
   handleInput(_: boolean, e: Event | InputEvent): void {
     let value = this.args.value ? [...this.args.value] : [];
diff --git a/packages/ember-toucan-core/src/components/form/fields/checkbox.hbs b/packages/ember-toucan-core/src/components/form/fields/checkbox.hbs
index 1e50d99fd..3fc32d5ff 100644
--- a/packages/ember-toucan-core/src/components/form/fields/checkbox.hbs
+++ b/packages/ember-toucan-core/src/components/form/fields/checkbox.hbs
@@ -25,14 +25,41 @@
         />
 
         <div class="flex flex-col">
-          <span
-            class="type-md-tight block leading-4
-              {{if @isDisabled 'text-disabled' 'text-titles-and-attributes'}}"
-            data-label
-          >{{@label}}</span>
+          {{#if
+            (this.assertBlockOrArgumentExists
+              (hash
+                blockExists=(has-block "label")
+                argName="label"
+                arg=@label
+                isRequired=true
+              )
+            )
+          }}
+            <span
+              class="type-md-tight block leading-4
+                {{if @isDisabled 'text-disabled' 'text-titles-and-attributes'}}"
+              data-label
+            >
+              {{#if (has-block "label")}}
+                {{yield to="label"}}
+              {{else}}
+                {{@label}}
+              {{/if}}
+            </span>
+          {{/if}}
 
-          {{#if @hint}}
-            <field.Hint data-hint>{{@hint}}</field.Hint>
+          {{#if
+            (this.assertBlockOrArgumentExists
+              (hash blockExists=(has-block "hint") argName="hint" arg=@hint)
+            )
+          }}
+            <field.Hint data-hint>
+              {{#if (has-block "hint")}}
+                {{yield to="hint"}}
+              {{else}}
+                {{@hint}}
+              {{/if}}
+            </field.Hint>
           {{/if}}
         </div>
       </label>
diff --git a/packages/ember-toucan-core/src/components/form/fields/checkbox.ts b/packages/ember-toucan-core/src/components/form/fields/checkbox.ts
index e01c65a8d..41383e560 100644
--- a/packages/ember-toucan-core/src/components/form/fields/checkbox.ts
+++ b/packages/ember-toucan-core/src/components/form/fields/checkbox.ts
@@ -1,6 +1,9 @@
 import Component from '@glimmer/component';
 import { assert } from '@ember/debug';
 
+import assertBlockOrArgumentExists from '../../../-private/assert-block-or-argument-exists';
+
+import type { AssertBlockOrArg } from '../../../-private/assert-block-or-argument-exists';
 import type { ErrorMessage } from '../../../-private/types';
 import type { ToucanFormCheckboxControlComponentSignature } from '../controls/checkbox';
 
@@ -37,7 +40,7 @@ export interface ToucanFormCheckboxFieldComponentSignature {
     /**
      * Provide a string to this argument to render inside of the label tag.
      */
-    label: string;
+    label?: string;
 
     /**
      * Sets the name attribute of the checkbox. A string specifying a name for the input control. This name is submitted along with the control's value when the form data is submitted.
@@ -73,15 +76,25 @@ export interface ToucanFormCheckboxFieldComponentSignature {
      */
     value?: string;
   };
+  Blocks: {
+    label: [];
+    hint: [];
+  };
 }
 
 export default class ToucanFormCheckboxFieldComponent extends Component<ToucanFormCheckboxFieldComponentSignature> {
+  assertBlockOrArgumentExists = ({
+    blockExists,
+    argName,
+    arg,
+    isRequired,
+  }: AssertBlockOrArg) =>
+    assertBlockOrArgumentExists({ blockExists, argName, arg, isRequired });
+
   constructor(
     owner: unknown,
     args: ToucanFormCheckboxFieldComponentSignature['Args']
   ) {
-    assert('A "@label" argument is required', args.label);
-
     assert(
       'Both "@value" and "@isChecked" arguments were supplied. "@value" is reserved for being used in a CheckboxGroupField to specify the value attribute, while "@value" sets the checked state of the checkbox. Please use either "@value" or "@isChecked", but not both.',
       !(args.isChecked && args.value)
diff --git a/packages/ember-toucan-core/src/components/form/fields/file-input.hbs b/packages/ember-toucan-core/src/components/form/fields/file-input.hbs
index b782f8a80..9e9b336a2 100644
--- a/packages/ember-toucan-core/src/components/form/fields/file-input.hbs
+++ b/packages/ember-toucan-core/src/components/form/fields/file-input.hbs
@@ -4,10 +4,39 @@
 >
   <Form::Field as |field|>
     <field.Label for={{field.id}}>
-      <span data-label>{{@label}}</span>
-      {{#if @hint}}
-        <field.Hint data-hint>{{@hint}}</field.Hint>
+      {{#if
+        (this.assertBlockOrArgumentExists
+          (hash
+            blockExists=(has-block "label")
+            argName="label"
+            arg=@label
+            isRequired=true
+          )
+        )
+      }}
+        <span data-label>
+          {{#if (has-block "label")}}
+            {{yield to="label"}}
+          {{else}}
+            {{@label}}
+          {{/if}}
+        </span>
       {{/if}}
+
+      {{#if
+        (this.assertBlockOrArgumentExists
+          (hash blockExists=(has-block "hint") argName="hint" arg=@hint)
+        )
+      }}
+        <field.Hint data-hint>
+          {{#if (has-block "hint")}}
+            {{yield to="hint"}}
+          {{else}}
+            {{@hint}}
+          {{/if}}
+        </field.Hint>
+      {{/if}}
+
       <field.Control class="mt-1.5">
         <Form::Controls::FileInput
           aria-describedby={{if this.hasError field.errorId}}
diff --git a/packages/ember-toucan-core/src/components/form/fields/file-input.ts b/packages/ember-toucan-core/src/components/form/fields/file-input.ts
index 490d6229e..4f158d9f8 100644
--- a/packages/ember-toucan-core/src/components/form/fields/file-input.ts
+++ b/packages/ember-toucan-core/src/components/form/fields/file-input.ts
@@ -2,6 +2,9 @@ import Component from '@glimmer/component';
 import { assert } from '@ember/debug';
 import { action } from '@ember/object';
 
+import assertBlockOrArgumentExists from '../../../-private/assert-block-or-argument-exists';
+
+import type { AssertBlockOrArg } from '../../../-private/assert-block-or-argument-exists';
 import type { ErrorMessage } from '../../../-private/types';
 
 type FileTarget = EventTarget & { files?: FileList };
@@ -15,25 +18,32 @@ interface ToucanFormFileInputFieldComponentSignature {
     error?: ErrorMessage;
     files?: File[];
     hint?: string;
-    label: string;
+    label?: string;
     trigger: string;
     isDisabled?: boolean;
     multiple?: boolean;
     onChange?: (files: File[], event: FileEvent) => void;
     rootTestSelector?: string;
   };
+  Blocks: {
+    label: [];
+    hint: [];
+  };
 }
 
 export default class ToucanFormFileInputFieldComponent extends Component<ToucanFormFileInputFieldComponentSignature> {
+  assertBlockOrArgumentExists = ({
+    blockExists,
+    argName,
+    arg,
+    isRequired,
+  }: AssertBlockOrArg) =>
+    assertBlockOrArgumentExists({ blockExists, argName, arg, isRequired });
+
   constructor(
     owner: unknown,
     args: ToucanFormFileInputFieldComponentSignature['Args']
   ) {
-    assert(
-      'A "@label" argument is required for Form::FileInput::Field',
-      args.label !== undefined
-    );
-
     assert(
       'A "@deleteLabel" argument is required for Form::FileInput::Field. This provides an accessible label for the delete button.',
       args.deleteLabel !== undefined
diff --git a/packages/ember-toucan-core/src/components/form/fields/input.hbs b/packages/ember-toucan-core/src/components/form/fields/input.hbs
index a9f00695f..c29852c2f 100644
--- a/packages/ember-toucan-core/src/components/form/fields/input.hbs
+++ b/packages/ember-toucan-core/src/components/form/fields/input.hbs
@@ -3,10 +3,37 @@
   data-root-field={{if @rootTestSelector @rootTestSelector null}}
 >
   <Form::Field as |field|>
-    <field.Label for={{field.id}} data-label>{{@label}}</field.Label>
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash
+          blockExists=(has-block "label")
+          argName="label"
+          arg=@label
+          isRequired=true
+        )
+      )
+    }}
+      <field.Label for={{field.id}} data-label>
+        {{#if (has-block "label")}}
+          {{yield to="label"}}
+        {{else}}
+          {{@label}}
+        {{/if}}
+      </field.Label>
+    {{/if}}
 
-    {{#if @hint}}
-      <field.Hint id={{field.hintId}} data-hint>{{@hint}}</field.Hint>
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash blockExists=(has-block "hint") argName="hint" arg=@hint)
+      )
+    }}
+      <field.Hint id={{field.hintId}} data-hint>
+        {{#if (has-block "hint")}}
+          {{yield to="hint"}}
+        {{else}}
+          {{@hint}}
+        {{/if}}
+      </field.Hint>
     {{/if}}
 
     <field.Control class="mt-1.5 flex">
diff --git a/packages/ember-toucan-core/src/components/form/fields/input.ts b/packages/ember-toucan-core/src/components/form/fields/input.ts
index d7698c77e..85fbee282 100644
--- a/packages/ember-toucan-core/src/components/form/fields/input.ts
+++ b/packages/ember-toucan-core/src/components/form/fields/input.ts
@@ -1,6 +1,8 @@
 import Component from '@glimmer/component';
-import { assert } from '@ember/debug';
 
+import assertBlockOrArgumentExists from '../../../-private/assert-block-or-argument-exists';
+
+import type { AssertBlockOrArg } from '../../../-private/assert-block-or-argument-exists';
 import type { ErrorMessage, OnChangeCallback } from '../../../-private/types';
 
 interface ToucanFormInputFieldComponentSignature {
@@ -24,7 +26,7 @@ interface ToucanFormInputFieldComponentSignature {
     /**
      * Provide a string to this argument to render inside of the label tag.
      */
-    label: string;
+    label?: string;
 
     /**
      * The function called when the element is typed into.
@@ -43,17 +45,19 @@ interface ToucanFormInputFieldComponentSignature {
   };
   Blocks: {
     default: [];
+    label: [];
+    hint: [];
   };
 }
 
 export default class ToucanFormInputFieldComponent extends Component<ToucanFormInputFieldComponentSignature> {
-  constructor(
-    owner: unknown,
-    args: ToucanFormInputFieldComponentSignature['Args']
-  ) {
-    assert('input field requires a label', args.label !== undefined);
-    super(owner, args);
-  }
+  assertBlockOrArgumentExists = ({
+    blockExists,
+    argName,
+    arg,
+    isRequired,
+  }: AssertBlockOrArg) =>
+    assertBlockOrArgumentExists({ blockExists, argName, arg, isRequired });
 
   get hasError() {
     return Boolean(this.args?.error);
diff --git a/packages/ember-toucan-core/src/components/form/fields/radio-group.hbs b/packages/ember-toucan-core/src/components/form/fields/radio-group.hbs
index 9cba86e9b..bfb609c69 100644
--- a/packages/ember-toucan-core/src/components/form/fields/radio-group.hbs
+++ b/packages/ember-toucan-core/src/components/form/fields/radio-group.hbs
@@ -1,22 +1,65 @@
-<Form::Fieldset
-  @label={{@label}}
-  @hint={{@hint}}
-  @error={{@error}}
-  @isDisabled={{@isDisabled}}
-  aria-invalid={{if @error "true"}}
-  aria-required="true"
-  role="radiogroup"
-  ...attributes
->
-  {{yield
-    (hash
-      RadioField=(component
-        (ensure-safe-component this.RadioFieldComponent)
-        name=@name
-        onChange=this.handleInput
-        selectedValue=@value
-        isDisabled=@isDisabled
+<Form::Field as |field|>
+  <fieldset
+    aria-describedby="{{if @error field.errorId}} {{if @hint field.hintId}}"
+    disabled={{@isDisabled}}
+    aria-invalid={{if @error "true"}}
+    aria-required="true"
+    role="radiogroup"
+    ...attributes
+  >
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash
+          blockExists=(has-block "label")
+          argName="label"
+          arg=@label
+          isRequired=true
+        )
       )
-    )
-  }}
-</Form::Fieldset>
\ No newline at end of file
+    }}
+      <legend class="type-md-tight text-body-and-labels block" data-label>
+        {{#if (has-block "label")}}
+          {{yield to="label"}}
+        {{else}}
+          {{@label}}
+        {{/if}}
+      </legend>
+    {{/if}}
+
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash blockExists=(has-block "hint") argName="hint" arg=@hint)
+      )
+    }}
+      <field.Hint id={{field.hintId}} data-hint>
+        {{#if (has-block "hint")}}
+          {{yield to="hint"}}
+        {{else}}
+          {{@hint}}
+        {{/if}}
+      </field.Hint>
+    {{/if}}
+
+    <field.Control
+      class="mt-1.5 flex flex-col space-y-2 rounded-sm p-1
+        {{if @error 'shadow-error-outline'}}"
+      data-control
+    >
+      {{yield
+        (hash
+          RadioField=(component
+            (ensure-safe-component this.RadioFieldComponent)
+            name=@name
+            onChange=this.handleInput
+            selectedValue=@value
+            isDisabled=@isDisabled
+          )
+        )
+      }}
+    </field.Control>
+
+    {{#if @error}}
+      <field.Error id={{field.errorId}} @error={{@error}} data-error />
+    {{/if}}
+  </fieldset>
+</Form::Field>
\ No newline at end of file
diff --git a/packages/ember-toucan-core/src/components/form/fields/radio-group.ts b/packages/ember-toucan-core/src/components/form/fields/radio-group.ts
index 32efd89aa..2477bd61f 100644
--- a/packages/ember-toucan-core/src/components/form/fields/radio-group.ts
+++ b/packages/ember-toucan-core/src/components/form/fields/radio-group.ts
@@ -1,8 +1,10 @@
 import Component from '@glimmer/component';
 import { action } from '@ember/object';
 
+import assertBlockOrArgumentExists from '../../../-private/assert-block-or-argument-exists';
 import RadioFieldComponent from './radio';
 
+import type { AssertBlockOrArg } from '../../../-private/assert-block-or-argument-exists';
 import type { ErrorMessage } from '../../../-private/types';
 import type { ToucanFormRadioFieldComponentSignature } from './radio';
 import type { WithBoundArgs } from '@glint/template';
@@ -28,7 +30,7 @@ export interface ToucanFormRadioGroupFieldComponentSignature {
     /**
      * Provide a string to this argument to render inside of the label tag.
      */
-    label: string;
+    label?: string;
 
     /**
      * Sets the name attribute of the radio. A string specifying a name for the input control. This name is submitted along with the control's value when the form data is submitted.
@@ -61,12 +63,22 @@ export interface ToucanFormRadioGroupFieldComponentSignature {
         >;
       }
     ];
+    label: [];
+    hint: [];
   };
 }
 
 export default class ToucanFormRadioGroupFieldComponent extends Component<ToucanFormRadioGroupFieldComponentSignature> {
   RadioFieldComponent = RadioFieldComponent;
 
+  assertBlockOrArgumentExists = ({
+    blockExists,
+    argName,
+    arg,
+    isRequired,
+  }: AssertBlockOrArg) =>
+    assertBlockOrArgumentExists({ blockExists, argName, arg, isRequired });
+
   @action
   handleInput(value: string, e: Event | InputEvent): void {
     this.args.onChange?.(value, e);
diff --git a/packages/ember-toucan-core/src/components/form/fields/radio.hbs b/packages/ember-toucan-core/src/components/form/fields/radio.hbs
index d141c5768..5c07dd035 100644
--- a/packages/ember-toucan-core/src/components/form/fields/radio.hbs
+++ b/packages/ember-toucan-core/src/components/form/fields/radio.hbs
@@ -17,14 +17,42 @@
         />
 
         <div class="flex flex-col">
-          <span
-            class="type-md-tight block leading-4
-              {{if @isDisabled 'text-disabled' 'text-titles-and-attributes'}}"
-            data-label
-          >{{@label}}</span>
 
-          {{#if @hint}}
-            <field.Hint data-hint>{{@hint}}</field.Hint>
+          {{#if
+            (this.assertBlockOrArgumentExists
+              (hash
+                blockExists=(has-block "label")
+                argName="label"
+                arg=@label
+                isRequired=true
+              )
+            )
+          }}
+            <span
+              class="type-md-tight block leading-4
+                {{if @isDisabled 'text-disabled' 'text-titles-and-attributes'}}"
+              data-label
+            >
+              {{#if (has-block "label")}}
+                {{yield to="label"}}
+              {{else}}
+                {{@label}}
+              {{/if}}
+            </span>
+          {{/if}}
+
+          {{#if
+            (this.assertBlockOrArgumentExists
+              (hash blockExists=(has-block "hint") argName="hint" arg=@hint)
+            )
+          }}
+            <field.Hint data-hint>
+              {{#if (has-block "hint")}}
+                {{yield to="hint"}}
+              {{else}}
+                {{@hint}}
+              {{/if}}
+            </field.Hint>
           {{/if}}
         </div>
       </label>
diff --git a/packages/ember-toucan-core/src/components/form/fields/radio.ts b/packages/ember-toucan-core/src/components/form/fields/radio.ts
index fa9aa2035..c731a1774 100644
--- a/packages/ember-toucan-core/src/components/form/fields/radio.ts
+++ b/packages/ember-toucan-core/src/components/form/fields/radio.ts
@@ -1,6 +1,9 @@
 import Component from '@glimmer/component';
 import { assert } from '@ember/debug';
 
+import assertBlockOrArgumentExists from '../../../-private/assert-block-or-argument-exists';
+
+import type { AssertBlockOrArg } from '../../../-private/assert-block-or-argument-exists';
 import type { ToucanFormRadioControlComponentSignature } from '../controls/radio';
 
 export interface ToucanFormRadioFieldComponentSignature {
@@ -19,7 +22,7 @@ export interface ToucanFormRadioFieldComponentSignature {
     /**
      * Provide a string to this argument to render inside of the label tag.
      */
-    label: string;
+    label?: string;
 
     /**
      * Sets the name attribute of the radio. A string specifying a name for the input control. This name is submitted along with the control's value when the form data is submitted.
@@ -49,14 +52,25 @@ export interface ToucanFormRadioFieldComponentSignature {
      */
     value: ToucanFormRadioControlComponentSignature['Args']['value'];
   };
+  Blocks: {
+    label: [];
+    hint: [];
+  };
 }
 
 export default class ToucanFormRadioFieldComponent extends Component<ToucanFormRadioFieldComponentSignature> {
+  assertBlockOrArgumentExists = ({
+    blockExists,
+    argName,
+    arg,
+    isRequired,
+  }: AssertBlockOrArg) =>
+    assertBlockOrArgumentExists({ blockExists, argName, arg, isRequired });
+
   constructor(
     owner: unknown,
     args: ToucanFormRadioFieldComponentSignature['Args']
   ) {
-    assert('A "@label" argument is required', args.label);
     assert('A "@name" argument is required', args.name);
     super(owner, args);
   }
diff --git a/packages/ember-toucan-core/src/components/form/fields/textarea.hbs b/packages/ember-toucan-core/src/components/form/fields/textarea.hbs
index a7f566e33..fd497ed4b 100644
--- a/packages/ember-toucan-core/src/components/form/fields/textarea.hbs
+++ b/packages/ember-toucan-core/src/components/form/fields/textarea.hbs
@@ -3,10 +3,37 @@
   data-root-field={{if @rootTestSelector @rootTestSelector}}
 >
   <Form::Field as |field|>
-    <field.Label for={{field.id}} data-label>{{@label}}</field.Label>
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash
+          blockExists=(has-block "label")
+          argName="label"
+          arg=@label
+          isRequired=true
+        )
+      )
+    }}
+      <field.Label for={{field.id}} data-label>
+        {{#if (has-block "label")}}
+          {{yield to="label"}}
+        {{else}}
+          {{@label}}
+        {{/if}}
+      </field.Label>
+    {{/if}}
 
-    {{#if @hint}}
-      <field.Hint id={{field.hintId}} data-hint>{{@hint}}</field.Hint>
+    {{#if
+      (this.assertBlockOrArgumentExists
+        (hash blockExists=(has-block "hint") argName="hint" arg=@hint)
+      )
+    }}
+      <field.Hint id={{field.hintId}} data-hint>
+        {{#if (has-block "hint")}}
+          {{yield to="hint"}}
+        {{else}}
+          {{@hint}}
+        {{/if}}
+      </field.Hint>
     {{/if}}
 
     <field.Control class="mt-1.5">
diff --git a/packages/ember-toucan-core/src/components/form/fields/textarea.ts b/packages/ember-toucan-core/src/components/form/fields/textarea.ts
index a624d7be8..cac1691a6 100644
--- a/packages/ember-toucan-core/src/components/form/fields/textarea.ts
+++ b/packages/ember-toucan-core/src/components/form/fields/textarea.ts
@@ -1,6 +1,8 @@
 import Component from '@glimmer/component';
-import { assert } from '@ember/debug';
 
+import assertBlockOrArgumentExists from '../../../-private/assert-block-or-argument-exists';
+
+import type { AssertBlockOrArg } from '../../../-private/assert-block-or-argument-exists';
 import type { ErrorMessage } from '../../../-private/types';
 import type { ToucanFormTextareaControlComponentSignature } from '../controls/textarea';
 
@@ -25,7 +27,7 @@ export interface ToucanFormTextareaFieldComponentSignature {
     /**
      * Provide a string to this argument to render inside of the label tag.
      */
-    label: string;
+    label?: string;
 
     /**
      * The function called when the element is typed into.
@@ -42,14 +44,25 @@ export interface ToucanFormTextareaFieldComponentSignature {
      */
     value?: ToucanFormTextareaControlComponentSignature['Args']['value'];
   };
+  Blocks: {
+    label: [];
+    hint: [];
+  };
 }
 
 export default class ToucanFormTextareaFieldComponent extends Component<ToucanFormTextareaFieldComponentSignature> {
+  assertBlockOrArgumentExists = ({
+    blockExists,
+    argName,
+    arg,
+    isRequired,
+  }: AssertBlockOrArg) =>
+    assertBlockOrArgumentExists({ blockExists, argName, arg, isRequired });
+
   constructor(
     owner: unknown,
     args: ToucanFormTextareaFieldComponentSignature['Args']
   ) {
-    assert('A "@label" argument is required', args.label);
     super(owner, args);
   }
 
diff --git a/packages/ember-toucan-core/src/components/form/fieldset.hbs b/packages/ember-toucan-core/src/components/form/fieldset.hbs
deleted file mode 100644
index 8dd652204..000000000
--- a/packages/ember-toucan-core/src/components/form/fieldset.hbs
+++ /dev/null
@@ -1,28 +0,0 @@
-<Form::Field as |field|>
-  <fieldset
-    aria-describedby="{{if @error field.errorId}} {{if @hint field.hintId}}"
-    disabled={{@isDisabled}}
-    ...attributes
-  >
-    <legend
-      class="type-md-tight text-body-and-labels block"
-      data-label
-    >{{@label}}</legend>
-
-    {{#if @hint}}
-      <field.Hint id={{field.hintId}} data-hint>{{@hint}}</field.Hint>
-    {{/if}}
-
-    <field.Control
-      class="mt-1.5 flex flex-col space-y-2 rounded-sm p-1
-        {{if @error 'shadow-error-outline'}}"
-      data-control
-    >
-      {{yield}}
-    </field.Control>
-
-    {{#if @error}}
-      <field.Error id={{field.errorId}} @error={{@error}} data-error />
-    {{/if}}
-  </fieldset>
-</Form::Field>
\ No newline at end of file
diff --git a/packages/ember-toucan-core/src/components/form/fieldset.ts b/packages/ember-toucan-core/src/components/form/fieldset.ts
deleted file mode 100644
index d2ac30861..000000000
--- a/packages/ember-toucan-core/src/components/form/fieldset.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import Component from '@glimmer/component';
-import { assert } from '@ember/debug';
-
-import type { ErrorMessage } from '../../-private/types';
-
-interface ToucanFormFieldsetComponentSignature {
-  Element: HTMLFieldSetElement;
-  Args: {
-    /**
-     * Provide a string or array of strings to this argument to render an error message and apply error styling to the Field.
-     */
-    error?: ErrorMessage;
-
-    /**
-     * Provide a string to this argument to render a hint message to help describe the control.
-     */
-    hint?: string;
-
-    /**
-     * Sets the disabled attribute on the fieldset.
-     */
-    isDisabled?: boolean;
-
-    /**
-     * Provide a string to this argument to render inside of the label tag.
-     */
-    label: string;
-  };
-  Blocks: {
-    default: [];
-  };
-}
-
-export default class ToucanFormFieldComponent extends Component<ToucanFormFieldsetComponentSignature> {
-  constructor(
-    owner: unknown,
-    args: ToucanFormFieldsetComponentSignature['Args']
-  ) {
-    assert('A "@label" argument is required', args.label);
-    super(owner, args);
-  }
-}
diff --git a/packages/ember-toucan-core/src/template-registry.ts b/packages/ember-toucan-core/src/template-registry.ts
index 14836232b..e765d22cd 100644
--- a/packages/ember-toucan-core/src/template-registry.ts
+++ b/packages/ember-toucan-core/src/template-registry.ts
@@ -12,14 +12,12 @@ import type InputFieldComponent from './components/form/fields/input';
 import type RadioFieldComponent from './components/form/fields/radio';
 import type RadioGroupFieldComponent from './components/form/fields/radio-group';
 import type TextareaFieldComponent from './components/form/fields/textarea';
-import type FieldsetComponent from './components/form/fieldset';
 import type FormFileInputDeleteButtonComponent from './components/form/file-input/delete-button';
 import type FormFileInputListComponent from './components/form/file-input/list';
 
 export default interface Registry {
   Button: typeof ButtonComponent;
   'Form::Field': typeof FieldComponent;
-  'Form::Fieldset': typeof FieldsetComponent;
   'Form::Fields::Checkbox': typeof CheckboxFieldComponent;
   'Form::Fields::CheckboxGroup': typeof CheckboxGroupFieldComponent;
   'Form::Fields::Input': typeof InputFieldComponent;
diff --git a/test-app/tests/integration/components/checkbox-field-test.gts b/test-app/tests/integration/components/checkbox-field-test.gts
index 1bcd3f5ff..92fa630ea 100644
--- a/test-app/tests/integration/components/checkbox-field-test.gts
+++ b/test-app/tests/integration/components/checkbox-field-test.gts
@@ -7,7 +7,7 @@ import { module, test } from 'qunit';
 import CheckboxField from '@crowdstrike/ember-toucan-core/components/form/fields/checkbox';
 import { setupRenderingTest } from 'test-app/tests/helpers';
 
-module('Integration | Component | CheckboxField', function (hooks) {
+module('Integration | Component | Fields | CheckboxField', function (hooks) {
   setupRenderingTest(hooks);
 
   test('it renders', async function (assert) {
@@ -45,6 +45,18 @@ module('Integration | Component | CheckboxField', function (hooks) {
     assert.dom('[data-hint]').hasText('Hint text');
   });
 
+  test('it renders with a hint and label block', async function (assert) {
+    await render(<template>
+      <CheckboxField data-checkbox>
+        <:label>label block content</:label>
+        <:hint>hint block content</:hint>
+      </CheckboxField>
+    </template>);
+
+    assert.dom('[data-hint]').hasText('hint block content');
+    assert.dom('[data-label]').hasText('label block content');
+  });
+
   test('it renders with an error', async function (assert) {
     await render(<template>
       <CheckboxField
@@ -219,14 +231,32 @@ module('Integration | Component | CheckboxField', function (hooks) {
 
     setupOnerror((e: Error) => {
       assert.ok(
-        e.message.includes('A "@label" argument is required'),
+        e.message.includes(
+          'Assertion Failed: You need either :label or @label'
+        ),
+        'Expected assertion error message'
+      );
+    });
+
+    await render(<template><CheckboxField /></template>);
+  });
+
+  test('it throws an assertion error if both `@label` and `:label` are provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.ok(
+        e.message.includes(
+          'Assertion Failed: You can have :label or @label, but not both'
+        ),
         'Expected assertion error message'
       );
     });
 
     await render(<template>
-      {{! @glint-expect-error: we are not providing @label, so this is expected }}
-      <CheckboxField />
+      <CheckboxField @label="Label">
+        <:label>label block content</:label>
+      </CheckboxField>
     </template>);
   });
 
diff --git a/test-app/tests/integration/components/checkbox-group-field-test.gts b/test-app/tests/integration/components/checkbox-group-field-test.gts
index 53c1d0430..6be1c718c 100644
--- a/test-app/tests/integration/components/checkbox-group-field-test.gts
+++ b/test-app/tests/integration/components/checkbox-group-field-test.gts
@@ -1,13 +1,13 @@
 /* eslint-disable no-undef -- Until https://github.com/ember-cli/eslint-plugin-ember/issues/1747 is resolved... */
 /* eslint-disable simple-import-sort/imports,padding-line-between-statements,decorator-position/decorator-position -- Can't fix these manually, without --fix working in .gts */
 
-import { click, render } from '@ember/test-helpers';
+import { click, render, setupOnerror } from '@ember/test-helpers';
 import { module, test } from 'qunit';
 
 import CheckboxGroupField from '@crowdstrike/ember-toucan-core/components/form/fields/checkbox-group';
 import { setupRenderingTest } from 'test-app/tests/helpers';
 
-module('Integration | Component | CheckboxGroupField', function (hooks) {
+module('Integration | Component | Fields | CheckboxGroup', function (hooks) {
   setupRenderingTest(hooks);
 
   test('it renders', async function (assert) {
@@ -113,6 +113,18 @@ module('Integration | Component | CheckboxGroupField', function (hooks) {
     assert.dom('[data-error]').hasText('Error text');
   });
 
+  test('it renders with a hint and label block', async function (assert) {
+    await render(<template>
+      <CheckboxGroupField @name="group">
+        <:label><span data-label>label block content</span></:label>
+        <:hint><span data-hint>hint block content</span></:hint>
+      </CheckboxGroupField>
+    </template>);
+
+    assert.dom('[data-label]').hasText('label block content');
+    assert.dom('[data-hint]').hasText('hint block content');
+  });
+
   test('it default-checks the checkbox with the matching `@value`', async function (assert) {
     let selectedOption = ['option-2'];
 
@@ -219,16 +231,37 @@ module('Integration | Component | CheckboxGroupField', function (hooks) {
     assert.dom('[data-checkbox-2]').isChecked();
   });
 
-  test('it sets "aria-invalid" when provided with `@error`', async function (assert) {
+  test('it throws an assertion error if no `@label` or `:label` is provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.ok(
+        e.message.includes(
+          'Assertion Failed: You need either :label or @label'
+        ),
+        'Expected assertion error message'
+      );
+    });
+
+    await render(<template><CheckboxGroupField @name="group" /></template>);
+  });
+
+  test('it throws an assertion error if both `@label` and `:label` are provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.ok(
+        e.message.includes(
+          'Assertion Failed: You can have :label or @label, but not both'
+        ),
+        'Expected assertion error message'
+      );
+    });
+
     await render(<template>
-      <CheckboxGroupField
-        @label="Label"
-        @name="group"
-        @error="Error message"
-        data-group-field
-      />
+      <CheckboxGroupField @label="Label" @name="group">
+        <:label>Label</:label>
+      </CheckboxGroupField>
     </template>);
-
-    assert.dom('[data-group-field]').hasAttribute('aria-invalid', 'true');
   });
 });
diff --git a/test-app/tests/integration/components/fieldset-test.gts b/test-app/tests/integration/components/fieldset-test.gts
deleted file mode 100644
index 8b0c81ecb..000000000
--- a/test-app/tests/integration/components/fieldset-test.gts
+++ /dev/null
@@ -1,132 +0,0 @@
-/* eslint-disable no-undef -- Until https://github.com/ember-cli/eslint-plugin-ember/issues/1747 is resolved... */
-/* eslint-disable simple-import-sort/imports,padding-line-between-statements,decorator-position/decorator-position -- Can't fix these manually, without --fix working in .gts */
-
-import { find, render, setupOnerror } from '@ember/test-helpers';
-import { module, test } from 'qunit';
-
-import Fieldset from '@crowdstrike/ember-toucan-core/components/form/fieldset';
-import { setupRenderingTest } from 'test-app/tests/helpers';
-
-module('Integration | Component | Fieldset', function (hooks) {
-  setupRenderingTest(hooks);
-
-  test('it renders', async function (assert) {
-    await render(<template>
-      <Fieldset @label="Label" data-fieldset />
-    </template>);
-
-    assert.dom('[data-label]').hasText('Label');
-
-    assert
-      .dom('[data-hint]')
-      .doesNotExist(
-        'Expected hint block not to be displayed as a hint was not provided'
-      );
-
-    assert
-      .dom('[data-error]')
-      .doesNotExist(
-        'Expected error block not to be displayed as an error was not provided'
-      );
-
-    assert.dom('[data-control]').hasNoClass('shadow-error-outline');
-  });
-
-  test('it renders with a hint', async function (assert) {
-    await render(<template>
-      <Fieldset @label="Label" @hint="Hint text" data-fieldset />
-    </template>);
-
-    assert.dom('[data-hint]').hasText('Hint text');
-    assert.dom('[data-hint]').hasAttribute('id');
-    assert.dom('[data-fieldset]').hasAttribute('aria-describedby');
-  });
-
-  test('it renders with an error', async function (assert) {
-    await render(<template>
-      <Fieldset @label="Label" @error="Error text" data-fieldset />
-    </template>);
-
-    assert.dom('[data-error]').hasText('Error text');
-    assert.dom('[data-error]').hasAttribute('id');
-
-    assert.dom('[data-fieldset]').hasAttribute('aria-describedby');
-
-    assert.dom('[data-control]').hasClass('shadow-error-outline');
-  });
-
-  test('it renders with multiple errors', async function (assert) {
-    let testErrors = ['error 1', 'error 2'];
-
-    await render(<template>
-      <Fieldset @label="Label" @error={{testErrors}} data-fieldset />
-    </template>);
-
-    assert.dom('[data-control]').hasClass('shadow-error-outline');
-
-    assert.dom('[data-error]').hasAttribute('id');
-
-    assert.dom('[data-error-item]').exists({ count: 2 });
-
-    assert
-      .dom('[data-error-item="0"]')
-      .hasText('error 1', 'Expected to have first error text rendered');
-
-    assert
-      .dom('[data-error-item="1"]')
-      .hasText('error 2', 'Expected to have first error text rendered');
-  });
-
-  test('it sets aria-describedby when both a hint and error are provided', async function (assert) {
-    await render(<template>
-      <Fieldset
-        @label="Label"
-        @error="Error text"
-        @hint="Hint text"
-        data-fieldset
-      />
-    </template>);
-
-    let errorId = find('[data-error]')?.getAttribute('id') || '';
-    assert.ok(errorId, 'Expected errorId to be truthy');
-
-    let hintId = find('[data-hint]')?.getAttribute('id') || '';
-    assert.ok(hintId, 'Expected hintId to be truthy');
-
-    assert
-      .dom('[data-fieldset]')
-      .hasAttribute('aria-describedby', `${errorId} ${hintId}`);
-  });
-
-  test('it disables the fieldset using `@isDisabled`', async function (assert) {
-    await render(<template>
-      <Fieldset @label="Label" @isDisabled={{true}} data-fieldset />
-    </template>);
-
-    assert.dom('[data-fieldset]').isDisabled();
-  });
-
-  test('it spreads attributes to the underlying fieldset', async function (assert) {
-    await render(<template>
-      <Fieldset @label="Label" form="form-id" data-fieldset />
-    </template>);
-
-    assert.dom('[data-fieldset]').hasAttribute('form', 'form-id');
-  });
-
-  test('it throws an assertion error if no `@label` is provided', async function (assert) {
-    assert.expect(1);
-
-    setupOnerror((e: Error) => {
-      assert.ok(
-        e.message.includes('A "@label" argument is required'),
-        'Expected assertion error message'
-      );
-    });
-
-    await render(<template>
-      {{! @glint-expect-error: we are not providing @label, so this is expected }}
-      <Fieldset />
-    </template>);
-  });
-});
diff --git a/test-app/tests/integration/components/file-input-field-test.gts b/test-app/tests/integration/components/file-input-field-test.gts
index 235ead737..03d086c47 100644
--- a/test-app/tests/integration/components/file-input-field-test.gts
+++ b/test-app/tests/integration/components/file-input-field-test.gts
@@ -39,7 +39,7 @@ const banana = createFile(['Upload file sample'], {
   type: 'text/plain',
 });
 
-module('Integration | Component | FileInputField', function (hooks) {
+module('Integration | Component | Fields | FileInput', function (hooks) {
   setupRenderingTest(hooks);
 
   function onChange() {
@@ -91,13 +91,24 @@ module('Integration | Component | FileInputField', function (hooks) {
       />
     </template>);
 
-    // For the file input field component, the only aria-describedby
-    // value should be the errorId.  This is due to the way the component
-    // is structured, where the label+hint are rendered inside of the
-    // wrapping <label> element
-    const hint = find('[data-hint]');
+    assert.dom('[data-hint]').hasText('Hint text');
+  });
+
+  test('it renders with a hint and label block', async function (assert) {
+    await render(<template>
+      <FileInputField
+        @deleteLabel="Delete File"
+        @trigger="Select Files"
+        @onChange={{onChange}}
+        data-file-input-field
+      >
+        <:label><span data-label>label block content</span></:label>
+        <:hint><span data-hint>hint block content</span></:hint>
+      </FileInputField>
+    </template>);
 
-    assert.dom(hint).hasText('Hint text');
+    assert.dom('[data-hint]').hasText('hint block content');
+    assert.dom('[data-label]').hasText('label block content');
   });
 
   test('it renders with an error', async function (assert) {
@@ -403,6 +414,7 @@ module('Integration | Component | FileInputField', function (hooks) {
       .hasText('avocado.txt', 'With multiple, files are additive');
     assert.dom('ul > li:nth-child(3) [data-file-name]').hasText('banana.txt');
   });
+
   test('it applies the provided @rootTestSelector to the data-root-field attribute', async function (assert) {
     await render(<template>
       <FileInputField
@@ -418,19 +430,48 @@ module('Integration | Component | FileInputField', function (hooks) {
     assert.dom('[data-root-field="selector"]').exists();
   });
 
-  test('it throws an assertion error if no @label is provided', async function (assert) {
+  test('it throws an assertion error if there is no @label or :label', async function (assert) {
     assert.expect(1);
 
     setupOnerror((e: Error) => {
       assert.ok(
-        e.message.includes('A "@label" argument is required'),
+        e.message.includes(
+          'Assertion Failed: You need either :label or @label'
+        ),
         'Expected assertion error message'
       );
     });
 
     await render(<template>
-      {{! @glint-expect-error: we are not providing @label, so this is expected }}
-      <FileInputField @onChange={{onChange}} />
+      <FileInputField
+        @deleteLabel="Delete file"
+        @trigger="Select Files"
+        @onChange={{onChange}}
+      />
+    </template>);
+  });
+
+  test('it throws an assertion error if both `@label` and `:label` are provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.ok(
+        e.message.includes(
+          'Assertion Failed: You can have :label or @label, but not both'
+        ),
+        'Expected assertion error message'
+      );
+    });
+
+    await render(<template>
+      <FileInputField
+        @deleteLabel="Delete file"
+        @trigger="Select Files"
+        @label="Label"
+        @onChange={{onChange}}
+      >
+        <:label>Hello</:label>
+      </FileInputField>
     </template>);
   });
 
diff --git a/test-app/tests/integration/components/input-field-test.gts b/test-app/tests/integration/components/input-field-test.gts
index 30a5d0c7a..25eca0349 100644
--- a/test-app/tests/integration/components/input-field-test.gts
+++ b/test-app/tests/integration/components/input-field-test.gts
@@ -1,4 +1,3 @@
-/* eslint-disable no-undef -- Until https://github.com/ember-cli/eslint-plugin-ember/issues/1747 is resolved... */
 /* eslint-disable simple-import-sort/imports,padding-line-between-statements,decorator-position/decorator-position -- Can't fix these manually, without --fix working in .gts */
 import { fillIn, find, render, setupOnerror } from '@ember/test-helpers';
 import { module, test } from 'qunit';
@@ -6,7 +5,7 @@ import { module, test } from 'qunit';
 import InputField from '@crowdstrike/ember-toucan-core/components/form/fields/input';
 import { setupRenderingTest } from 'test-app/tests/helpers';
 
-module('Integration | Component | InputField', function (hooks) {
+module('Integration | Component | Fields | Input', function (hooks) {
   setupRenderingTest(hooks);
 
   test('it renders', async function (assert) {
@@ -28,19 +27,33 @@ module('Integration | Component | InputField', function (hooks) {
     assert.dom(input).hasNoClass('shadow-error-outline');
   });
 
-  test('it encourages @label via assert', async function (assert) {
+  test('it throws an assertion error if no `@label` or `:label` is provided', async function (assert) {
     assert.expect(1);
 
     setupOnerror((e: Error) => {
       assert.strictEqual(
         e.message,
-        'Assertion Failed: input field requires a label',
-        'Expected an error message if @label is not provided'
+        'Assertion Failed: You need either :label or @label',
+        'Expected assertion error message'
+      );
+    });
+    await render(<template><InputField type="text" /></template>);
+  });
+
+  test('it throws an assertion error if both `@label` and `:label` are provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.strictEqual(
+        e.message,
+        'Assertion Failed: You can have :label or @label, but not both',
+        'Expected assertion error message'
       );
     });
     await render(<template>
-      {{! @glint-expect-error: should have an error here for missing @label }}
-      <InputField type="text" />
+      <InputField @label="Label" type="text">
+        <:label>Hello</:label>
+      </InputField>
     </template>);
   });
 
@@ -110,6 +123,18 @@ module('Integration | Component | InputField', function (hooks) {
     );
   });
 
+  test('it renders a hint and label block', async function (assert) {
+    await render(<template>
+      <InputField type="text" data-input>
+        <:label><span data-label>label block content</span></:label>
+        <:hint><span data-hint>hint block content</span></:hint>
+      </InputField>
+    </template>);
+
+    assert.dom('[data-label]').hasText('label block content');
+    assert.dom('[data-hint]').hasText('hint block content');
+  });
+
   test('it sets aria-describedby when both a hint and error are provided using the hint and error ids', async function (assert) {
     await render(<template>
       <InputField
diff --git a/test-app/tests/integration/components/radio-field-test.gts b/test-app/tests/integration/components/radio-field-test.gts
index bd3f62075..e44f6bd5b 100644
--- a/test-app/tests/integration/components/radio-field-test.gts
+++ b/test-app/tests/integration/components/radio-field-test.gts
@@ -7,7 +7,7 @@ import { module, test } from 'qunit';
 import RadioField from '@crowdstrike/ember-toucan-core/components/form/fields/radio';
 import { setupRenderingTest } from 'test-app/tests/helpers';
 
-module('Integration | Component | RadioField', function (hooks) {
+module('Integration | Component | Fields | Radio', function (hooks) {
   setupRenderingTest(hooks);
 
   test('it renders', async function (assert) {
@@ -48,6 +48,18 @@ module('Integration | Component | RadioField', function (hooks) {
     assert.dom('[data-hint]').hasText('Hint text');
   });
 
+  test('it renders with a hint and label block', async function (assert) {
+    await render(<template>
+      <RadioField @value="option" @name="name" data-radio>
+        <:label><span data-label>label block content</span></:label>
+        <:hint><span data-hint>hint block content</span></:hint>
+      </RadioField>
+    </template>);
+
+    assert.dom('[data-hint]').hasText('hint block content');
+    assert.dom('[data-label]').hasText('label block content');
+  });
+
   test('it sets the "for" attribute on the label to the "id" attribute of the radio', async function (assert) {
     await render(<template>
       <RadioField @value="option" @label="Label" @name="name" data-radio />
@@ -182,17 +194,37 @@ module('Integration | Component | RadioField', function (hooks) {
 
     setupOnerror((e: Error) => {
       assert.ok(
-        e.message.includes('A "@label" argument is required'),
+        e.message.includes(
+          'Assertion Failed: You need either :label or @label'
+        ),
         'Expected assertion error message'
       );
     });
 
     await render(<template>
-      {{! @glint-expect-error: we are not providing @label, so this is expected }}
       <RadioField @value="option" @name="name" />
     </template>);
   });
 
+  test('it throws an assertion error if both `@label` and `:label` are provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.ok(
+        e.message.includes(
+          'Assertion Failed: You can have :label or @label, but not both'
+        ),
+        'Expected assertion error message'
+      );
+    });
+
+    await render(<template>
+      <RadioField @label="Label" @value="option" @name="name">
+        <:label>Hello there</:label>
+      </RadioField>
+    </template>);
+  });
+
   test('it throws an assertion error if no `@value` is provided', async function (assert) {
     assert.expect(1);
 
diff --git a/test-app/tests/integration/components/radio-group-field-test.gts b/test-app/tests/integration/components/radio-group-field-test.gts
index 76b61cbf9..e18ca7aa1 100644
--- a/test-app/tests/integration/components/radio-group-field-test.gts
+++ b/test-app/tests/integration/components/radio-group-field-test.gts
@@ -1,13 +1,13 @@
 /* eslint-disable no-undef -- Until https://github.com/ember-cli/eslint-plugin-ember/issues/1747 is resolved... */
 /* eslint-disable simple-import-sort/imports,padding-line-between-statements,decorator-position/decorator-position -- Can't fix these manually, without --fix working in .gts */
 
-import { click, render } from '@ember/test-helpers';
+import { click, render, setupOnerror } from '@ember/test-helpers';
 import { module, test } from 'qunit';
 
 import RadioGroupField from '@crowdstrike/ember-toucan-core/components/form/fields/radio-group';
 import { setupRenderingTest } from 'test-app/tests/helpers';
 
-module('Integration | Component | RadioGroupField', function (hooks) {
+module('Integration | Component | Fields | RadioGroup', function (hooks) {
   setupRenderingTest(hooks);
 
   test('it renders', async function (assert) {
@@ -63,6 +63,18 @@ module('Integration | Component | RadioGroupField', function (hooks) {
     assert.dom('[data-hint]').hasText('Hint text');
   });
 
+  test('it renders with a hint and label block', async function (assert) {
+    await render(<template>
+      <RadioGroupField @name="group">
+        <:label><span data-label>label block content</span></:label>
+        <:hint><span data-hint>hint block content</span></:hint>
+      </RadioGroupField>
+    </template>);
+
+    assert.dom('[data-hint]').hasText('hint block content');
+    assert.dom('[data-label]').hasText('label block content');
+  });
+
   test('it renders with an error', async function (assert) {
     await render(<template>
       <RadioGroupField @label="Label" @name="group" @error="Error text" />
@@ -157,4 +169,36 @@ module('Integration | Component | RadioGroupField', function (hooks) {
 
     assert.dom('[data-group-field]').hasAttribute('aria-invalid', 'true');
   });
+
+  test('it throws an assertion error if no `@label` or `:label` is provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.strictEqual(
+        e.message,
+        'Assertion Failed: You need either :label or @label',
+        'Expected assertion error message'
+      );
+    });
+    await render(<template>
+      <RadioGroupField @name="group" type="text" />
+    </template>);
+  });
+
+  test('it throws an assertion error if both `@label` and `:label` are provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.strictEqual(
+        e.message,
+        'Assertion Failed: You can have :label or @label, but not both',
+        'Expected assertion error message'
+      );
+    });
+    await render(<template>
+      <RadioGroupField @name="group" @label="Label" type="text">
+        <:label>Hello</:label>
+      </RadioGroupField>
+    </template>);
+  });
 });
diff --git a/test-app/tests/integration/components/textarea-field-test.gts b/test-app/tests/integration/components/textarea-field-test.gts
index ed9511578..4d5ba9c0a 100644
--- a/test-app/tests/integration/components/textarea-field-test.gts
+++ b/test-app/tests/integration/components/textarea-field-test.gts
@@ -7,7 +7,7 @@ import { module, test } from 'qunit';
 import TextareaField from '@crowdstrike/ember-toucan-core/components/form/fields/textarea';
 import { setupRenderingTest } from 'test-app/tests/helpers';
 
-module('Integration | Component | TextareaField', function (hooks) {
+module('Integration | Component | Fields | Textarea', function (hooks) {
   setupRenderingTest(hooks);
 
   test('it renders', async function (assert) {
@@ -55,6 +55,18 @@ module('Integration | Component | TextareaField', function (hooks) {
     );
   });
 
+  test('it renders with a hint and label block', async function (assert) {
+    await render(<template>
+      <TextareaField data-textarea>
+        <:label><span data-label>label block content</span></:label>
+        <:hint><span data-hint>hint block content</span></:hint>
+      </TextareaField>
+    </template>);
+
+    assert.dom('[data-hint]').hasText('hint block content');
+    assert.dom('[data-label]').hasText('label block content');
+  });
+
   test('it renders with an error', async function (assert) {
     await render(<template>
       <TextareaField @label="Label" @error="Error text" data-textarea />
@@ -174,14 +186,32 @@ module('Integration | Component | TextareaField', function (hooks) {
 
     setupOnerror((e: Error) => {
       assert.ok(
-        e.message.includes('A "@label" argument is required'),
+        e.message.includes(
+          'Assertion Failed: You need either :label or @label'
+        ),
+        'Expected assertion error message'
+      );
+    });
+
+    await render(<template><TextareaField /></template>);
+  });
+
+  test('it throws an assertion error if a `@label` and :label are provided', async function (assert) {
+    assert.expect(1);
+
+    setupOnerror((e: Error) => {
+      assert.ok(
+        e.message.includes(
+          'Assertion Failed: You can have :label or @label, but not both'
+        ),
         'Expected assertion error message'
       );
     });
 
     await render(<template>
-      {{! @glint-expect-error: we are not providing @label, so this is expected }}
-      <TextareaField />
+      <TextareaField @label="Label">
+        <:label>Label</:label>
+      </TextareaField>
     </template>);
   });
 });