isRadio() — Custom Validation Function

Add additional constraints on which radio option is valid.

import { isRadio } from '@andresclua/validate'

const radios = document.querySelectorAll('.c--form-radio-a__item')
const config = {
    customValidation: (radios) => {
        const selected = Array.from(radios).find(r => r.checked)
        return {
            isValid: selected?.value !== 's',
            errorMessage: 'Small size is currently out of stock'
        }
    }
}

// On Change
radios.forEach(radio => {
    radio.addEventListener('change', () => {
        const result = isRadio({ elements: radios, config })
        errorEl.textContent = result.isValid ? '' : result.errorMessage
    })
})

// On Submit
validateBtn.addEventListener('click', () => {
    const result = isRadio({ elements: radios, config })
    errorEl.textContent = result.isValid ? '' : result.errorMessage
})
<div class="c--form-group-a">
  <div class="c--form-radio-a">
    <input type="radio" id="option-1" name="size" class="c--form-radio-a__item" value="s">
    <label class="c--form-radio-a__title" for="option-1">Small</label>
  </div>
  <div class="c--form-radio-a">
    <input type="radio" id="option-2" name="size" class="c--form-radio-a__item" value="m">
    <label class="c--form-radio-a__title" for="option-2">Medium</label>
  </div>
  <span class="c--form-error-a"></span>
</div>
/* Orientative — feel free to adapt to your own conventions */

.c--form-group-a {
    margin-bottom: 1.5em;
}

.c--form-radio-a {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 0.5em;
}

.c--form-radio-a__item {
    width: 16px;
    height: 16px;
    cursor: pointer;
    accent-color: currentColor;
}

.c--form-radio-a__title {
    font-size: 1em;
    cursor: pointer;
}

.c--form-error-a {
    color: red;
    font-size: 0.875em;
    display: none;
}
.c--form-error-a:not(:empty) { display: block; }
customValidation  function  Receives the NodeList of radios, returns { isValid, errorMessage }
Playground

On Change

On Submit