Volume 3: Human-System Collaboration

Pattern 13: Conditional Requirements

Part II: Interaction Patterns - Relationship Patterns


Opening Scenario: The Form That Demanded Too Much

Angela was filling out a job application. The form had a field marked as required:

* Spouse's Name: [________________]
  Required field

Angela wasn't married. She left it blank and clicked Submit.

Error: Spouse's Name is required

She stared at the screen, frustrated. "I don't have a spouse!"

She tried typing "N/A" just to get past the validation. It worked, and she submitted the form.

The HR manager, Carol, reviewed the application and was confused. "Why did Angela write 'N/A' for spouse's name? Is she being sarcastic?"

Carol looked at the form definition:

// Bad: Field is always required
{
  name: 'spouseName',
  label: "Spouse's Name",
  required: true  // Always!
}

"This is wrong," Carol said. "Spouse name should only be required if the person is married."


Carol redesigned the form:

// Better: Conditional requirement
{
  name: 'maritalStatus',
  type: 'select',
  options: ['Single', 'Married', 'Divorced', 'Widowed'],
  required: true
},
{
  name: 'spouseName',
  label: "Spouse's Name",
  required: {
    when: 'maritalStatus',
    equals: 'Married'
  }
}

Now the form behaved intelligently:

User selects "Single": - Spouse name field is hidden (not required) - No validation error

User selects "Married": - Spouse name field appears - Marked as required (red asterisk) - Validation enforces requirement

But Carol discovered more conditional requirements throughout the application:

Employment section:

If "Currently Employed" = Yes:
  → "Current Employer" is REQUIRED
  → "Start Date" is REQUIRED
  → "Supervisor Name" is REQUIRED

If "Currently Employed" = No:
  → These fields are OPTIONAL (or hidden)

Education section:

If "Degree Type" = "Bachelor's" or higher:
  → "Major" is REQUIRED
  → "Graduation Year" is REQUIRED

If "Degree Type" = "High School":
  → "Major" is NOT APPLICABLE
  → "Graduation Year" is REQUIRED

References section:

If "Years of Experience" >= 5:
  → 3 professional references REQUIRED

If "Years of Experience" < 5:
  → 1 professional reference REQUIRED

Carol built a system to handle all these cases:

class ConditionalRequirementEngine {
  constructor() {
    this.rules = new Map();
  }

  defineRule(fieldName, requirement) {
    this.rules.set(fieldName, requirement);
  }

  isRequired(fieldName, formData) {
    const rule = this.rules.get(fieldName);

    if (!rule) {
      return false;  // No rule = not required
    }

    if (rule === true) {
      return true;  // Unconditionally required
    }

    // Conditional requirement
    return this.evaluateCondition(rule, formData);
  }

  evaluateCondition(condition, formData) {
    if (condition.when) {
      const watchedField = condition.when;
      const watchedValue = formData[watchedField];

      if (condition.equals !== undefined) {
        return watchedValue === condition.equals;
      }

      if (condition.in !== undefined) {
        return condition.in.includes(watchedValue);
      }

      if (condition.greaterThan !== undefined) {
        return watchedValue > condition.greaterThan;
      }
    }

    return false;
  }
}

The new form was dynamic: - Fields appeared only when relevant - Requirements changed based on answers - No "N/A" workarounds needed - Clear visual indication of required fields

Application completion rate improved 35%. Error rates dropped 60%. And Angela's next application went smoothly.

Context

Conditional Requirements applies when:

Relevance depends on previous answers: Field only matters if user answered a certain way

Complexity must be hidden: Don't show irrelevant fields upfront

Business rules vary: Requirements change based on scenario

User fatigue is a risk: Long forms need to hide unnecessary fields

Data quality matters: Collecting data that doesn't apply leads to garbage

Workflow branches: Different paths require different information

Regulatory compliance: Some fields required only in specific jurisdictions

Problem Statement

Most forms handle requirements poorly, creating frustration and errors:

Always-required fields that sometimes don't apply:

// Bad: Always required, regardless of context
{
  name: 'spouseName',
  required: true
}

// User who isn't married gets error
// Forces fake data entry ("N/A", "None", etc.)

Hidden complexity in validation:

// Bad: Logic buried in validation function
function validate(form) {
  if (form.maritalStatus === 'Married' && !form.spouseName) {
    return "Spouse name is required for married applicants";
  }
}

// User doesn't know field is required until submit
// No visual indication in the form

Static forms that don't adapt:

<!-- Bad: All fields always visible and required -->
<label>Spouse's Name *</label>
<input name="spouseName" required>

<label>Spouse's Employer *</label>
<input name="spouseEmployer" required>

<label>Spouse's Income *</label>
<input name="spouseIncome" required>

<!-- Single people forced to fill these out -->

No visual feedback:

// Bad: Requirement changes but UI doesn't update
if (maritalStatus === 'Married') {
  // Spouse name is now required
  // But form still shows it as optional
}

Inconsistent behavior:

// Bad: Some fields hide, others just become optional
if (employmentStatus === 'Unemployed') {
  // Current employer field is hidden
  // But supervisor field just becomes optional
  // Inconsistent!
}

We need forms that show only relevant fields, clearly indicate dynamic requirements, and adapt based on user input.

Forces

Simplicity vs Completeness

  • Hide irrelevant fields to reduce clutter
  • But need all necessary information
  • Balance minimal form with comprehensive data

Immediate vs Delayed Indication

  • Show required indicator immediately?
  • Or wait until field becomes relevant?
  • Balance early warning with clarity

Hiding vs Disabling

  • Hide irrelevant fields entirely?
  • Or show them disabled?
  • Balance clean interface with context

Strict vs Forgiving

  • Enforce all conditional rules strictly?
  • Or allow some flexibility?
  • Balance data quality with user experience

Progressive vs All-at-Once

  • Add requirements progressively as user answers?
  • Or show all possible requirements upfront?
  • Balance discoverability with overwhelm

Solution

Define field requirements as conditional rules that evaluate based on form state, update visual indicators dynamically, and communicate changes clearly to users - making relevance obvious through progressive disclosure and adaptive validation.

The pattern has four key strategies:

1. Declarative Requirement Rules

Define requirements as data, not code:

class RequirementRuleEngine {
  constructor() {
    this.fieldRules = new Map();
  }

  defineField(fieldName, config) {
    this.fieldRules.set(fieldName, {
      label: config.label,
      type: config.type,
      required: config.required || false,
      visible: config.visible !== false,
      validation: config.validation
    });
  }

  // Simple conditional requirement
  whenEquals(fieldName, watchField, value) {
    const field = this.fieldRules.get(fieldName);
    field.required = {
      type: 'equals',
      watch: watchField,
      value: value
    };
  }

  // Multiple condition requirement
  whenAny(fieldName, watchField, values) {
    const field = this.fieldRules.get(fieldName);
    field.required = {
      type: 'in',
      watch: watchField,
      values: values
    };
  }

  // Complex condition requirement
  whenCondition(fieldName, conditionFn) {
    const field = this.fieldRules.get(fieldName);
    field.required = {
      type: 'function',
      evaluate: conditionFn
    };
  }

  // Compound conditions (AND/OR)
  whenAll(fieldName, conditions) {
    const field = this.fieldRules.get(fieldName);
    field.required = {
      type: 'and',
      conditions: conditions
    };
  }

  whenAnyOf(fieldName, conditions) {
    const field = this.fieldRules.get(fieldName);
    field.required = {
      type: 'or',
      conditions: conditions
    };
  }

  // Example: Job application rules
  defineJobApplicationRules() {
    // Spouse name required only if married
    this.defineField('spouseName', {
      label: "Spouse's Name",
      type: 'text'
    });
    this.whenEquals('spouseName', 'maritalStatus', 'Married');

    // Current employer required if employed
    this.defineField('currentEmployer', {
      label: 'Current Employer',
      type: 'text'
    });
    this.whenEquals('currentEmployer', 'employmentStatus', 'Employed');

    // References required based on experience
    this.defineField('references', {
      label: 'Professional References',
      type: 'list'
    });
    this.whenCondition('references', (form) => {
      const years = parseInt(form.yearsExperience);
      return years >= 5 ? 3 : 1;  // 3 refs if 5+ years, else 1
    });

    // Degree details required for college education
    this.defineField('major', {
      label: 'Major',
      type: 'text'
    });
    this.whenAny('major', 'degreeType', [
      "Bachelor's",
      "Master's",
      "Doctorate"
    ]);
  }
}

2. Dynamic Requirement Evaluation

Continuously evaluate requirements as form changes:

class DynamicRequirementEvaluator {
  constructor(ruleEngine) {
    this.ruleEngine = ruleEngine;
    this.currentRequirements = new Map();
  }

  evaluate(formData) {
    const updates = [];

    this.ruleEngine.fieldRules.forEach((config, fieldName) => {
      const wasRequired = this.currentRequirements.get(fieldName);
      const isRequired = this.isRequired(fieldName, formData, config);

      if (wasRequired !== isRequired) {
        updates.push({
          field: fieldName,
          wasRequired,
          isRequired,
          reason: this.getRequirementReason(config, formData)
        });

        this.currentRequirements.set(fieldName, isRequired);
      }
    });

    return updates;
  }

  isRequired(fieldName, formData, config) {
    const requirement = config.required;

    // Always required
    if (requirement === true) {
      return true;
    }

    // Never required
    if (!requirement || requirement === false) {
      return false;
    }

    // Conditional requirement
    return this.evaluateCondition(requirement, formData);
  }

  evaluateCondition(requirement, formData) {
    switch(requirement.type) {
      case 'equals':
        return formData[requirement.watch] === requirement.value;

      case 'in':
        return requirement.values.includes(formData[requirement.watch]);

      case 'function':
        return requirement.evaluate(formData);

      case 'and':
        return requirement.conditions.every(cond => 
          this.evaluateCondition(cond, formData)
        );

      case 'or':
        return requirement.conditions.some(cond => 
          this.evaluateCondition(cond, formData)
        );

      case 'not':
        return !this.evaluateCondition(requirement.condition, formData);

      case 'comparison':
        return this.evaluateComparison(
          formData[requirement.watch],
          requirement.operator,
          requirement.value
        );

      default:
        return false;
    }
  }

  evaluateComparison(fieldValue, operator, compareValue) {
    switch(operator) {
      case '>': return fieldValue > compareValue;
      case '>=': return fieldValue >= compareValue;
      case '<': return fieldValue < compareValue;
      case '<=': return fieldValue <= compareValue;
      case '!=': return fieldValue !== compareValue;
      case 'contains': return fieldValue.includes(compareValue);
      case 'startsWith': return fieldValue.startsWith(compareValue);
      default: return false;
    }
  }

  getRequirementReason(config, formData) {
    const req = config.required;

    if (req.type === 'equals') {
      return `Required when ${req.watch} is "${req.value}"`;
    }

    if (req.type === 'in') {
      return `Required when ${req.watch} is one of: ${req.values.join(', ')}`;
    }

    if (req.type === 'function') {
      return 'Required based on your answers';
    }

    return 'Required';
  }

  // Get all currently required fields
  getRequiredFields() {
    const required = [];
    this.currentRequirements.forEach((isReq, fieldName) => {
      if (isReq) required.push(fieldName);
    });
    return required;
  }

  // Validate form based on current requirements
  validateRequired(formData) {
    const errors = [];

    this.currentRequirements.forEach((isRequired, fieldName) => {
      if (isRequired && !formData[fieldName]) {
        const config = this.ruleEngine.fieldRules.get(fieldName);
        errors.push({
          field: fieldName,
          message: `${config.label} is required`,
          reason: this.getRequirementReason(config, formData)
        });
      }
    });

    return errors;
  }
}

3. Visual Requirement Indicators

Update UI to show dynamic requirements:

class RequirementVisualizer {
  constructor(evaluator) {
    this.evaluator = evaluator;
  }

  // Update required indicator on field
  updateFieldIndicator(fieldName, isRequired, animated = true) {
    const fieldElement = document.getElementById(fieldName);
    const labelElement = document.querySelector(`label[for="${fieldName}"]`);

    if (!labelElement) return;

    // Remove existing indicator
    const existing = labelElement.querySelector('.required-indicator');
    if (existing) existing.remove();

    if (isRequired) {
      // Add required indicator
      const indicator = document.createElement('span');
      indicator.className = 'required-indicator';
      indicator.textContent = '*';
      indicator.setAttribute('aria-label', 'required');

      if (animated) {
        indicator.classList.add('fade-in');
      }

      labelElement.appendChild(indicator);

      // Update field attributes
      fieldElement.setAttribute('required', 'required');
      fieldElement.setAttribute('aria-required', 'true');

      // Add visual styling
      fieldElement.classList.add('required');
    } else {
      // Remove required state
      fieldElement.removeAttribute('required');
      fieldElement.setAttribute('aria-required', 'false');
      fieldElement.classList.remove('required');
    }
  }

  // Show notification about requirement change
  showRequirementChange(update) {
    const message = update.isRequired
      ? `${this.getFieldLabel(update.field)} is now required`
      : `${this.getFieldLabel(update.field)} is no longer required`;

    this.showToast(message, 'info', {
      duration: 3000,
      reason: update.reason
    });

    // Scroll field into view if newly required
    if (update.isRequired) {
      const fieldElement = document.getElementById(update.field);
      if (fieldElement && !this.isInViewport(fieldElement)) {
        this.scrollToField(update.field, { 
          highlight: true,
          delay: 500 
        });
      }
    }
  }

  // Highlight required fields that are empty
  highlightMissingRequired(errors) {
    errors.forEach(error => {
      const fieldElement = document.getElementById(error.field);
      const container = fieldElement.closest('.field-container');

      // Add error styling
      container.classList.add('error');

      // Show inline error message
      const errorMsg = document.createElement('div');
      errorMsg.className = 'field-error';
      errorMsg.textContent = error.message;

      if (error.reason) {
        const reason = document.createElement('div');
        reason.className = 'field-error-reason';
        reason.textContent = error.reason;
        errorMsg.appendChild(reason);
      }

      container.appendChild(errorMsg);
    });
  }

  // Show summary of required fields
  showRequiredSummary() {
    const required = this.evaluator.getRequiredFields();

    if (required.length === 0) {
      return null;
    }

    const summary = document.createElement('div');
    summary.className = 'required-fields-summary';
    summary.innerHTML = `
      <div class="summary-header">
        <span class="icon">📋</span>
        <span class="title">Required Fields (${required.length})</span>
      </div>
      <ul class="required-list">
        ${required.map(field => `
          <li>
            <a href="#${field}" onclick="scrollToField('${field}')">
              ${this.getFieldLabel(field)}
            </a>
          </li>
        `).join('')}
      </ul>
    `;

    return summary;
  }

  // Progressive disclosure: show fields as they become relevant
  showField(fieldName, reason) {
    const container = document.getElementById(`${fieldName}-container`);

    if (container && container.style.display === 'none') {
      // Slide in animation
      container.style.display = 'block';
      container.classList.add('slide-in');

      // Add context message
      const contextMsg = document.createElement('div');
      contextMsg.className = 'field-context';
      contextMsg.textContent = reason;
      container.insertBefore(contextMsg, container.firstChild);

      // Remove context message after delay
      setTimeout(() => contextMsg.remove(), 5000);
    }
  }

  hideField(fieldName) {
    const container = document.getElementById(`${fieldName}-container`);

    if (container) {
      container.classList.add('slide-out');
      setTimeout(() => {
        container.style.display = 'none';
        container.classList.remove('slide-out');
      }, 300);
    }
  }

  getFieldLabel(fieldName) {
    const labelElement = document.querySelector(`label[for="${fieldName}"]`);
    return labelElement ? labelElement.textContent.replace('*', '').trim() : fieldName;
  }
}

4. Conditional Visibility

Show/hide fields based on relevance:

class ConditionalVisibility {
  constructor(ruleEngine, evaluator) {
    this.ruleEngine = ruleEngine;
    this.evaluator = evaluator;
    this.visibilityRules = new Map();
  }

  defineVisibility(fieldName, condition) {
    this.visibilityRules.set(fieldName, condition);
  }

  // Example: Show spouse fields only if married
  showSpouseFields() {
    const spouseFields = [
      'spouseName',
      'spouseEmployer',
      'spouseIncome'
    ];

    spouseFields.forEach(field => {
      this.defineVisibility(field, {
        when: 'maritalStatus',
        equals: 'Married'
      });
    });
  }

  // Example: Show international address fields for non-US
  showInternationalFields() {
    const intlFields = [
      'province',
      'postalCode',
      'country'
    ];

    intlFields.forEach(field => {
      this.defineVisibility(field, {
        when: 'addressType',
        equals: 'International'
      });
    });
  }

  evaluateVisibility(formData) {
    const updates = [];

    this.visibilityRules.forEach((condition, fieldName) => {
      const wasVisible = this.isVisible(fieldName);
      const shouldBeVisible = this.evaluator.evaluateCondition(
        condition,
        formData
      );

      if (wasVisible !== shouldBeVisible) {
        updates.push({
          field: fieldName,
          visible: shouldBeVisible
        });
      }
    });

    return updates;
  }

  isVisible(fieldName) {
    const container = document.getElementById(`${fieldName}-container`);
    return container && container.style.display !== 'none';
  }

  applyVisibilityUpdates(updates) {
    updates.forEach(update => {
      const container = document.getElementById(`${update.field}-container`);

      if (container) {
        if (update.visible) {
          container.style.display = 'block';
          container.classList.add('fade-in');
        } else {
          container.classList.add('fade-out');
          setTimeout(() => {
            container.style.display = 'none';
            container.classList.remove('fade-out');

            // Clear field value when hidden
            const field = document.getElementById(update.field);
            if (field) field.value = '';
          }, 300);
        }
      }
    });
  }
}

Implementation Details

Complete Conditional Requirements System

class ConditionalRequirementsSystem {
  constructor() {
    this.ruleEngine = new RequirementRuleEngine();
    this.evaluator = new DynamicRequirementEvaluator(this.ruleEngine);
    this.visualizer = new RequirementVisualizer(this.evaluator);
    this.visibility = new ConditionalVisibility(this.ruleEngine, this.evaluator);
  }

  initialize(formDefinition) {
    // Define all field rules
    formDefinition.fields.forEach(field => {
      this.ruleEngine.defineField(field.name, field);

      // Set up conditional requirements
      if (field.requiredWhen) {
        this.applyRequirementCondition(field.name, field.requiredWhen);
      }

      // Set up conditional visibility
      if (field.visibleWhen) {
        this.visibility.defineVisibility(field.name, field.visibleWhen);
      }
    });

    // Initial evaluation
    this.evaluateAll({});

    // Listen for changes
    this.setupChangeListeners();
  }

  applyRequirementCondition(fieldName, condition) {
    if (condition.equals) {
      this.ruleEngine.whenEquals(fieldName, condition.field, condition.equals);
    } else if (condition.in) {
      this.ruleEngine.whenAny(fieldName, condition.field, condition.in);
    } else if (condition.function) {
      this.ruleEngine.whenCondition(fieldName, condition.function);
    }
  }

  setupChangeListeners() {
    document.addEventListener('change', (e) => {
      const formData = this.getFormData();
      this.evaluateAll(formData);
    });

    // Also listen on input for immediate feedback
    document.addEventListener('input', (e) => {
      const formData = this.getFormData();
      this.evaluateAll(formData);
    });
  }

  evaluateAll(formData) {
    // Evaluate requirement changes
    const reqUpdates = this.evaluator.evaluate(formData);

    // Update visual indicators
    reqUpdates.forEach(update => {
      this.visualizer.updateFieldIndicator(
        update.field,
        update.isRequired,
        true
      );

      // Optionally show notification
      if (update.isRequired && !update.wasRequired) {
        this.visualizer.showRequirementChange(update);
      }
    });

    // Evaluate visibility changes
    const visUpdates = this.visibility.evaluateVisibility(formData);
    this.visibility.applyVisibilityUpdates(visUpdates);
  }

  validateForm() {
    const formData = this.getFormData();
    const errors = this.evaluator.validateRequired(formData);

    if (errors.length > 0) {
      this.visualizer.highlightMissingRequired(errors);
      return false;
    }

    return true;
  }

  getFormData() {
    const form = document.querySelector('form');
    const data = new FormData(form);
    return Object.fromEntries(data.entries());
  }
}

// Example usage
const system = new ConditionalRequirementsSystem();

system.initialize({
  fields: [
    {
      name: 'maritalStatus',
      label: 'Marital Status',
      type: 'select',
      options: ['Single', 'Married', 'Divorced', 'Widowed'],
      required: true
    },
    {
      name: 'spouseName',
      label: "Spouse's Name",
      type: 'text',
      requiredWhen: {
        field: 'maritalStatus',
        equals: 'Married'
      },
      visibleWhen: {
        when: 'maritalStatus',
        equals: 'Married'
      }
    },
    {
      name: 'employmentStatus',
      label: 'Employment Status',
      type: 'select',
      options: ['Employed', 'Unemployed', 'Self-Employed', 'Retired'],
      required: true
    },
    {
      name: 'currentEmployer',
      label: 'Current Employer',
      type: 'text',
      requiredWhen: {
        field: 'employmentStatus',
        in: ['Employed', 'Self-Employed']
      },
      visibleWhen: {
        when: 'employmentStatus',
        in: ['Employed', 'Self-Employed']
      }
    }
  ]
});

Consequences

Benefits

Reduced Form Clutter: - Only show relevant fields - Progressive disclosure - Less overwhelming for users

Improved Data Quality: - No fake "N/A" entries - Fields collect data only when applicable - Clearer user intent

Better User Experience: - Clear what's required when - No surprise requirements at submit - Form adapts to user's situation

Faster Completion: - Users skip irrelevant sections - Less time on form - Higher completion rates

Enforced Business Rules: - Requirements match policies - Consistent application - Automated compliance

Liabilities

Complexity: - Conditional logic can get intricate - Testing all paths challenging - Debugging harder than static forms

User Confusion: - "Why did this become required?" - Need clear communication - Visual changes must be obvious

Performance: - Real-time evaluation adds overhead - Complex conditions can be slow - Need optimization for large forms

Maintenance: - Business rules change - Conditions need updating - Documentation critical

Accessibility: - Screen readers need notification of changes - Keyboard navigation affected by show/hide - Must maintain focus management

Domain Examples

Healthcare: Patient Intake

// Conditional requirements based on insurance
defineHealthcareRules() {
  // Insurance fields
  this.whenEquals('insurancePolicyNumber', 'hasInsurance', 'Yes');
  this.whenEquals('insuranceGroupNumber', 'hasInsurance', 'Yes');

  // Secondary insurance only if primary exists
  this.whenEquals('secondaryInsurance', 'hasInsurance', 'Yes');

  // Pharmacy required for prescription medications
  this.whenEquals('preferredPharmacy', 'needsPrescriptions', 'Yes');

  // Emergency contact required for minors
  this.whenCondition('emergencyContact', (form) => {
    const age = this.calculateAge(form.birthDate);
    return age < 18;
  });
}
// Conditional requirements based on case type
defineLegalCaseRules() {
  // Opposing party required for litigation
  this.whenAny('opposingParty', 'caseType', [
    'Civil Litigation',
    'Family Law',
    'Employment Dispute'
  ]);

  // Children details required for family law
  this.whenEquals('childrenNames', 'caseType', 'Family Law');
  this.whenEquals('custodyArrangement', 'caseType', 'Family Law');

  // Corporate details for business cases
  this.whenAny('companyEIN', 'caseType', [
    'Business Formation',
    'Corporate Law',
    'Commercial Litigation'
  ]);
}

Real Estate: Property Listing

// Conditional requirements based on property type
defineRealEstateRules() {
  // HOA information required for condos/townhouses
  this.whenAny('hoaFees', 'propertyType', ['Condo', 'Townhouse']);
  this.whenAny('hoaName', 'propertyType', ['Condo', 'Townhouse']);

  // Lot size required for land/single-family
  this.whenAny('lotSize', 'propertyType', ['Land', 'Single Family']);

  // Rental history required for investment properties
  this.whenEquals('rentalIncome', 'listingType', 'Investment');
  this.whenEquals('leaseTerms', 'listingType', 'Investment');
}

Prerequisites: - Volume 3, Pattern 1: Progressive Disclosure (hide/show based on progress)

Synergies: - Volume 3, Pattern 9: Contextual Constraints (what's valid depends on requirements) - Volume 3, Pattern 12: Mutual Exclusivity (exclusive choices affect requirements) - Volume 3, Pattern 14: Cross-Field Validation (validate conditional requirements)

Conflicts: - All-at-once forms (some contexts need everything visible) - Print forms (can't dynamically show/hide)

Alternatives: - Wizard workflows (one step per condition) - Multiple forms (separate form for each scenario) - All fields optional (let backend sort it out)

Known Uses

Tax Software (TurboTax, H&R Block): Show tax forms based on income types

Insurance Applications: Require different fields based on coverage type

Job Applications: Require references based on experience level

Loan Applications: Require co-signer based on credit score

Survey Tools (SurveyMonkey, Typeform): Skip logic and conditional questions

E-commerce: Require shipping address only for physical products

Healthcare Portals (Epic MyChart): Require different info based on appointment type


Further Reading

Academic Foundations

  • Adaptive Systems: Oppermann, R., & Rasher, R. (1997). "Adaptability and Adaptivity in Learning Systems." Knowledge Transfer 2: 173-179.
  • Business Rules: von Halle, B. (2001). Business Rules Applied: Building Better Systems Using the Business Rules Approach. Wiley. ISBN: 978-0471412939
  • Form Design Research: Jarrett, C., & Gaffney, G. (2009). Forms that Work: Designing Web Forms for Usability. Morgan Kaufmann. ISBN: 978-1558607101

Practical Implementation

Standards & Specifications

  • Pattern 11: Validation Rules - Requirements are validation rules
  • Pattern 14: Contextual Constraints - Context determines requirements
  • Pattern 17: Mutual Exclusivity - Exclusive choices change requirements
  • Pattern 19: Cross-Field Validation - Validate conditional requirements
  • Pattern 26: Dynamic UI Adaptation - Show/hide required fields
  • Volume 2, Chapter 5: Privacy-Preserving Observation - Context triggers requirements
  • Volume 1, Chapter 7: Cross-Domain Analysis - Rule-driven requirements

Tools & Libraries

Implementation Examples