radio-group

Native radio input grouping with role and focus behavior.

Use a native fieldset with a legend and radio inputs. AngularCSS recognizes the semantic group and styles its native radio descendants directly.

<fieldset>
  <div class="field" orientation="horizontal">
    <input id="default" name="density" type="radio" value="default" />
    <label for="default">Default</label>
  </div>
</fieldset>

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Radio Group</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="density='comfortable'"
    data-example="radio-group-demo"
  >
    <fieldset class="visual-example">
      <legend class="visually-hidden">Density</legend>

      <div orientation="horizontal" class="field">
        <input
          id="density-default"
          name="density"
          type="radio"
          value="default"
          ng-model="density"
        />
        <label for="density-default">Default</label>
      </div>
      <div orientation="horizontal" class="field">
        <input
          id="density-comfortable"
          name="density"
          type="radio"
          value="comfortable"
          ng-model="density"
        />
        <label for="density-comfortable">Comfortable</label>
      </div>
      <div orientation="horizontal" class="field">
        <input
          id="density-compact"
          name="density"
          type="radio"
          value="compact"
          ng-model="density"
        />
        <label for="density-compact">Compact</label>
      </div>

      <output class="visually-hidden" aria-live="polite">
        Selected: <span ng-bind="density"></span>
      </output>
    </fieldset>
  </body>
</html>

Reference Workflows

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Radio Group Workflows</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="planType='plus'; densityDescription='comfortable'; disabledChoice='option2'; subscription='monthly'; notification='email'; rtlDensity='comfortable'"
    data-example="radio-group-choice-card radio-group-description radio-group-disabled radio-group-fieldset radio-group-invalid radio-group-rtl"
  >
    <main class="radio-group-workflows">
      <section class="radio-workflow-section" aria-label="Plan choice cards">
        <fieldset class="radio-choice-list">
          <label for="plus-plan" class="radio-choice">
            <div orientation="horizontal" class="field">
              <span>
                <strong>Plus</strong>
                <small> For individuals and small teams. </small>
              </span>
              <input
                id="plus-plan"
                name="plan-type"
                type="radio"
                value="plus"
                ng-model="planType"
              />
            </div>
          </label>
          <label for="pro-plan" class="radio-choice">
            <div orientation="horizontal" class="field">
              <span>
                <strong>Pro</strong>
                <small> For growing businesses. </small>
              </span>
              <input
                id="pro-plan"
                name="plan-type"
                type="radio"
                value="pro"
                ng-model="planType"
              />
            </div>
          </label>
          <label for="enterprise-plan" class="radio-choice">
            <div orientation="horizontal" class="field">
              <span>
                <strong>Enterprise</strong>
                <small> For large teams and enterprises. </small>
              </span>
              <input
                id="enterprise-plan"
                name="plan-type"
                type="radio"
                value="enterprise"
                ng-model="planType"
              />
            </div>
          </label>
        </fieldset>
      </section>

      <section class="radio-workflow-section" aria-label="Density descriptions">
        <fieldset class="radio-description-list">
          <div orientation="horizontal" class="field">
            <input
              id="desc-r1"
              name="density-description"
              type="radio"
              value="default"
              ng-model="densityDescription"
            />
            <section>
              <label for="desc-r1">Default</label>
              <p>Standard spacing for most use cases.</p>
            </section>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="desc-r2"
              name="density-description"
              type="radio"
              value="comfortable"
              ng-model="densityDescription"
            />
            <section>
              <label for="desc-r2">Comfortable</label>
              <p>More space between elements.</p>
            </section>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="desc-r3"
              name="density-description"
              type="radio"
              value="compact"
              ng-model="densityDescription"
            />
            <section>
              <label for="desc-r3">Compact</label>
              <p>Minimal spacing for dense layouts.</p>
            </section>
          </div>
        </fieldset>
      </section>

      <section class="radio-workflow-section" aria-label="Disabled radio">
        <fieldset class="radio-simple-list">
          <div orientation="horizontal" class="field">
            <input
              id="disabled-1"
              name="disabled-choice"
              type="radio"
              value="option1"
              disabled
              ng-model="disabledChoice"
            />
            <label for="disabled-1" class="radio-plain-label"> Disabled </label>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="disabled-2"
              name="disabled-choice"
              type="radio"
              value="option2"
              ng-model="disabledChoice"
            />
            <label for="disabled-2" class="radio-plain-label"> Option 2 </label>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="disabled-3"
              name="disabled-choice"
              type="radio"
              value="option3"
              ng-model="disabledChoice"
            />
            <label for="disabled-3" class="radio-plain-label"> Option 3 </label>
          </div>
        </fieldset>
      </section>

      <section
        class="radio-workflow-section"
        aria-label="Subscription fieldset"
      >
        <fieldset class="radio-fieldset field-set">
          <legend variant="label">Subscription Plan</legend>
          <p>Yearly and lifetime plans offer significant savings.</p>

          <div orientation="horizontal" class="field">
            <input
              id="plan-monthly"
              name="subscription"
              type="radio"
              value="monthly"
              ng-model="subscription"
            />
            <label for="plan-monthly" class="radio-plain-label">
              Monthly ($9.99/month)
            </label>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="plan-yearly"
              name="subscription"
              type="radio"
              value="yearly"
              ng-model="subscription"
            />
            <label for="plan-yearly" class="radio-plain-label">
              Yearly ($99.99/year)
            </label>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="plan-lifetime"
              name="subscription"
              type="radio"
              value="lifetime"
              ng-model="subscription"
            />
            <label for="plan-lifetime" class="radio-plain-label">
              Lifetime ($299.99)
            </label>
          </div>
        </fieldset>
      </section>

      <section class="radio-workflow-section" aria-label="Invalid radio group">
        <fieldset class="radio-fieldset field-set">
          <legend variant="label">Notification Preferences</legend>
          <p>Choose how you want to receive notifications.</p>

          <div orientation="horizontal" class="field">
            <input
              id="invalid-email"
              name="notification"
              type="radio"
              value="email"
              aria-invalid="true"
              ng-model="notification"
            />
            <label for="invalid-email" class="radio-plain-label">
              Email only
            </label>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="invalid-sms"
              name="notification"
              type="radio"
              value="sms"
              aria-invalid="true"
              ng-model="notification"
            />
            <label for="invalid-sms" class="radio-plain-label">
              SMS only
            </label>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="invalid-both"
              name="notification"
              type="radio"
              value="both"
              aria-invalid="true"
              ng-model="notification"
            />
            <label for="invalid-both" class="radio-plain-label">
              Both Email &amp; SMS
            </label>
          </div>
        </fieldset>
      </section>

      <section
        class="radio-workflow-section"
        aria-label="RTL density options"
        dir="rtl"
        lang="ar"
      >
        <fieldset class="radio-description-list" dir="rtl">
          <div orientation="horizontal" class="field">
            <input
              id="r1-rtl"
              name="rtl-density"
              type="radio"
              value="default"
              dir="rtl"
              ng-model="rtlDensity"
            />
            <section>
              <label for="r1-rtl" dir="rtl">افتراضي</label>
              <p dir="rtl">تباعد قياسي لمعظم حالات الاستخدام.</p>
            </section>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="r2-rtl"
              name="rtl-density"
              type="radio"
              value="comfortable"
              dir="rtl"
              ng-model="rtlDensity"
            />
            <section>
              <label for="r2-rtl" dir="rtl">مريح</label>
              <p dir="rtl">مساحة أكبر بين العناصر.</p>
            </section>
          </div>
          <div orientation="horizontal" class="field">
            <input
              id="r3-rtl"
              name="rtl-density"
              type="radio"
              value="compact"
              dir="rtl"
              ng-model="rtlDensity"
            />
            <section>
              <label for="r3-rtl" dir="rtl">مضغوط</label>
              <p dir="rtl">تباعد أدنى للتخطيطات الكثيفة.</p>
            </section>
          </div>
        </fieldset>
      </section>
    </main>
  </body>
</html>

Field Compositions

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Radio Fields</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="radioFields={subscription:'free',battery:'',security:'',unlock:'',invalid:'',disabled:''}"
    data-example="radio-fields"
  >
    <main class="radio-fields-demo visual-example field-group">
      <fieldset class="field-set">
        <legend variant="label" id="subscription-title">
          Subscription Plan
        </legend>

        <div orientation="horizontal" class="field">
          <input
            id="radio-free"
            name="subscription-plan"
            type="radio"
            value="free"
            ng-model="radioFields.subscription"
          />
          <label for="radio-free" class="radio-plain-label"> Free Plan </label>
        </div>
        <div orientation="horizontal" class="field">
          <input
            id="radio-pro"
            name="subscription-plan"
            type="radio"
            value="pro"
            ng-model="radioFields.subscription"
          />
          <label for="radio-pro" class="radio-plain-label"> Pro Plan </label>
        </div>
        <div orientation="horizontal" class="field">
          <input
            id="radio-enterprise"
            name="subscription-plan"
            type="radio"
            value="enterprise"
            ng-model="radioFields.subscription"
          />
          <label for="radio-enterprise" class="radio-plain-label">
            Enterprise
          </label>
        </div>
      </fieldset>

      <fieldset class="field-set" aria-describedby="battery-description">
        <legend variant="label" id="battery-title">Battery Level</legend>
        <p id="battery-description">Choose your preferred battery level.</p>

        <div orientation="horizontal" class="field">
          <input
            id="battery-high"
            name="battery-level"
            type="radio"
            value="high"
            ng-model="radioFields.battery"
          />
          <label for="battery-high">High</label>
        </div>
        <div orientation="horizontal" class="field">
          <input
            id="battery-medium"
            name="battery-level"
            type="radio"
            value="medium"
            ng-model="radioFields.battery"
          />
          <label for="battery-medium">Medium</label>
        </div>
        <div orientation="horizontal" class="field">
          <input
            id="battery-low"
            name="battery-level"
            type="radio"
            value="low"
            ng-model="radioFields.battery"
          />
          <label for="battery-low">Low</label>
        </div>
      </fieldset>

      <fieldset class="radio-fields-content-list" aria-label="Device security">
        <div orientation="horizontal" class="field">
          <input
            id="radio-content-1"
            name="device-security"
            type="radio"
            value="touch"
            ng-model="radioFields.security"
          />
          <section>
            <label for="radio-content-1">Enable Touch ID</label>
            <p>Enable Touch ID to quickly unlock your device.</p>
          </section>
        </div>
        <div orientation="horizontal" class="field">
          <input
            id="radio-content-2"
            name="device-security"
            type="radio"
            value="biometric"
            ng-model="radioFields.security"
          />
          <section>
            <label for="radio-content-2">
              Enable Touch ID and Face ID to make it even faster to unlock your
              device. This is a long label to test the layout.
            </label>
            <p>Enable Touch ID to quickly unlock your device.</p>
          </section>
        </div>
      </fieldset>

      <fieldset class="radio-fields-title-list" aria-label="Device unlock">
        <label for="radio-title-1">
          <div orientation="horizontal" class="field">
            <input
              id="radio-title-1"
              name="device-unlock"
              type="radio"
              value="touch"
              ng-model="radioFields.unlock"
            />
            <span>
              <strong>Enable Touch ID</strong>
              <small> Enable Touch ID to quickly unlock your device. </small>
            </span>
          </div>
        </label>
        <label for="radio-title-2">
          <div orientation="horizontal" class="field">
            <input
              id="radio-title-2"
              name="device-unlock"
              type="radio"
              value="biometric"
              ng-model="radioFields.unlock"
            />
            <span>
              <strong>
                Enable Touch ID and Face ID to make it even faster to unlock
                your device. This is a long label to test the layout.
              </strong>
              <small> Enable Touch ID to quickly unlock your device. </small>
            </span>
          </div>
        </label>
      </fieldset>

      <fieldset class="field-set">
        <legend variant="label" id="invalid-title">Invalid Radio Group</legend>

        <div orientation="horizontal" class="field">
          <input
            id="radio-invalid-1"
            name="invalid-choice"
            type="radio"
            value="invalid1"
            aria-invalid="true"
            ng-model="radioFields.invalid"
          />
          <label for="radio-invalid-1"> Invalid Option 1 </label>
        </div>
        <div orientation="horizontal" class="field">
          <input
            id="radio-invalid-2"
            name="invalid-choice"
            type="radio"
            value="invalid2"
            aria-invalid="true"
            ng-model="radioFields.invalid"
          />
          <label for="radio-invalid-2"> Invalid Option 2 </label>
        </div>
      </fieldset>

      <fieldset class="field-set" disabled>
        <legend variant="label" id="disabled-title">
          Disabled Radio Group
        </legend>

        <div orientation="horizontal" class="field">
          <input
            id="radio-disabled-1"
            name="disabled-choice"
            type="radio"
            value="disabled1"
            ng-model="radioFields.disabled"
          />
          <label for="radio-disabled-1"> Disabled Option 1 </label>
        </div>
        <div orientation="horizontal" class="field">
          <input
            id="radio-disabled-2"
            name="disabled-choice"
            type="radio"
            value="disabled2"
            ng-model="radioFields.disabled"
          />
          <label for="radio-disabled-2"> Disabled Option 2 </label>
        </div>
      </fieldset>
    </main>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. Native fieldset and radio behavior.

Anatomy

Root styling selector

  • fieldset:has(input[type="radio"]):not(.toggle-group)

Semantic structure

Use a native fieldset with a legend. Place labeled input type="radio" controls inside it and give related controls the same name. AngularCSS styles the fieldset from that semantic structure.

API

Attributes and state

AttributeAccessPurpose
aria-invalidAuthoredValidation state exposed to assistive technology and CSS.
checkedAuthoredInitial native checked state.
dirAuthoredText and interaction direction: ltr or rtl.
disabledAuthoredDisables native or component interaction.
nameAuthoredAuthored HTML attribute or styling hook.
requiredAuthoredMarks a native form value as required.
valueAuthoredNative value or authored component value.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

A native fieldset and legend group radio inputs sharing one name. The browser owns selection, arrow-key behavior, disabled state, validation, and form submission; AngularTS ng-model owns application state. AngularCSS registers no radio-group directive.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Use native radio inputs with a shared name and an explicit label for every control. Use fieldset and legend for a visible group label when appropriate. Preserve native disabled and invalid semantics, and connect supporting descriptions with aria-describedby.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.