Documentation Index Fetch the complete documentation index at: https://docs-terra.withunify.org/llms.txt
Use this file to discover all available pages before exploring further.
Eligibility Screener
The screener is a guided questionnaire that collects household information and progressively matches users to benefit programs.
Design Philosophy
The screener follows three principles:
Start with location : Geography determines which programs are available
Show progress immediately : Update match counts after each step
Allow incompleteness : Users can skip questions and still get useful results
Step-by-Step Breakdown
Step 1: Location
Purpose : Filter to geographically available programs
interface LocationStep {
zipCode : string ;
// Derived from geocoding:
state : string ;
countyFips : string ;
city ?: string ;
coordinates : { lat : number ; lng : number };
}
UI Component : ZIP code input with auto-geocoding
< LocationStep
value = { screenerData . zipCode }
onChange = { ( location ) => {
updateScreener ({
zipCode: location . zipCode ,
state: location . state ,
countyFips: location . fips ,
});
} }
/>
What happens :
User enters ZIP code
Geocoding resolves to coordinates
Reverse geocoding determines county FIPS
Programs filtered to federal + state + county scope
Step 2: Household Composition
Purpose : Determine household size for FPL calculations and family-based programs
interface HouseholdStep {
householdSize : number ;
adults : number ;
children : number ;
childrenAges ?: number []; // For age-specific programs
}
UI Component : Numeric inputs with validation
< HouseholdStep
onComplete = { ( data ) => {
updateScreener ({
householdSize: data . adults + data . children ,
adults: data . adults ,
children: data . children ,
childrenAges: data . childrenAges ,
});
} }
/>
Programs affected :
WIC (children under 5, pregnant women)
Head Start (children 3-5)
CHIP (children under 19)
Free/Reduced Lunch (school-age children)
Step 3: Income
Purpose : Apply income-based eligibility rules
interface IncomeStep {
hasIncome : boolean ;
grossMonthlyIncome ?: number ;
netMonthlyIncome ?: number ;
incomeTypes ?: IncomeType [];
}
UI Component : Income calculator with breakdowns
< IncomeStep
householdSize = { screenerData . householdSize }
onComplete = { ( income ) => {
updateScreener ({
hasIncome: income . hasIncome ,
grossMonthlyIncome: income . gross ,
netMonthlyIncome: income . net ,
});
} }
/>
Income types tracked :
Wages and salaries
Self-employment
Social Security (retirement)
SSI/SSDI (disability)
Unemployment benefits
Child support/alimony
Pension/retirement
Investment income
Rental income
FPL Display :
< FPLIndicator
monthlyIncome = { 3000 }
householdSize = { 4 }
/>
// Shows: "115% of Federal Poverty Level"
Step 4: Demographics
Purpose : Apply categorical eligibility rules
interface DemographicsStep {
citizenshipStatus : CitizenshipStatus ;
hasElderly : boolean ; // Anyone 60+ in household
hasDisabled : boolean ; // Anyone with disability
isPregnant : boolean ;
isVeteran : boolean ;
isStudent : boolean ;
}
UI Component : Multiple choice questions
< DemographicsStep
questions = { [
{
id: "citizenship" ,
label: "What is your citizenship status?" ,
options: [
{ value: "citizen" , label: "U.S. Citizen" },
{ value: "permanent_resident" , label: "Permanent Resident (Green Card)" },
{ value: "qualified_alien" , label: "Qualified Immigrant" },
{ value: "refugee" , label: "Refugee or Asylee" },
{ value: "other" , label: "Other / Prefer not to say" },
],
},
// ...
] }
/>
Programs affected by demographics :
Demographic Programs Elderly (60+) Medicare, Senior SNAP, Meals on Wheels Disabled SSI, SSDI, Medicaid, Vocational Rehab Pregnant WIC, Medicaid, TANF Veteran VA Healthcare, GI Bill, VA Pension Student Pell Grant, Work-Study, SNAP student rules
Step 5: Current Benefits (Optional)
Purpose : Apply categorical eligibility and avoid duplicate suggestions
interface CurrentBenefitsStep {
receivingBenefits : boolean ;
currentBenefits : string [];
}
UI Component : Checkbox list
< CurrentBenefitsStep
options = { [
{ value: "snap" , label: "SNAP / Food Stamps" },
{ value: "medicaid" , label: "Medicaid" },
{ value: "tanf" , label: "TANF / Cash Assistance" },
{ value: "ssi" , label: "SSI (Supplemental Security Income)" },
{ value: "ssdi" , label: "SSDI (Social Security Disability)" },
{ value: "section8" , label: "Section 8 / Housing Voucher" },
{ value: "wic" , label: "WIC" },
{ value: "liheap" , label: "LIHEAP / Utility Assistance" },
] }
/>
Categorical eligibility triggers :
SNAP → Free school meals, Lifeline, WIC
TANF → SNAP, Free meals, Medicaid
SSI → SNAP, Medicaid, Lifeline
Medicaid → WIC, Lifeline
Step 6: Results
Purpose : Display matched programs with actions
< ScreenerResults
results = { matchResults }
onSaveProgram = { ( programId ) => saveToUser ( programId ) }
onStartApplication = { ( programId ) => startApplication ( programId ) }
/>
Result grouping :
< ResultsDisplay >
< ResultGroup
title = "Likely Eligible"
description = "Based on your answers, you appear to qualify"
programs = { results . filter ( r => r . eligibility === "likely" ) }
variant = "success"
/>
< ResultGroup
title = "Possibly Eligible"
description = "You may qualify - check program details"
programs = { results . filter ( r => r . eligibility === "possible" ) }
variant = "info"
/>
< ResultGroup
title = "Worth Exploring"
description = "Programs in your area you might qualify for later"
programs = { results . filter ( r => r . eligibility === "unlikely" ) }
variant = "muted"
collapsed
/>
</ ResultsDisplay >
State Management
Screener state persists across sessions using localStorage and database sync.
interface ScreenerState {
// Current progress
currentStep : number ;
completedSteps : number [];
// Collected data
data : Partial < HouseholdData >;
// Match results (cached)
matchResults ?: MatchResult [];
lastMatchedAt ?: Date ;
// Metadata
startedAt : Date ;
lastUpdatedAt : Date ;
}
// Hook for screener state
function useScreener () {
const [ state , setState ] = useState < ScreenerState >(() => {
// Load from localStorage on mount
const saved = localStorage . getItem ( "pathfinder_screener" );
return saved ? JSON . parse ( saved ) : initialState ;
});
// Auto-save on changes
useEffect (() => {
localStorage . setItem ( "pathfinder_screener" , JSON . stringify ( state ));
}, [ state ]);
// Sync to database when logged in
useEffect (() => {
if ( user ) {
syncScreenerToProfile ( user . id , state . data );
}
}, [ user , state . data ]);
return { state , updateScreener , resetScreener };
}
Real-Time Match Updates
Matches update after each step with debouncing.
function useMatchUpdates ( screenerData : Partial < HouseholdData >) {
const [ matches , setMatches ] = useState < MatchResult []>([]);
const [ isLoading , setIsLoading ] = useState ( false );
// Debounced match calculation
const debouncedMatch = useMemo (
() =>
debounce ( async ( data : Partial < HouseholdData >) => {
setIsLoading ( true );
const results = await matchPrograms ( data );
setMatches ( results );
setIsLoading ( false );
}, 300 ),
[]
);
useEffect (() => {
debouncedMatch ( screenerData );
}, [ screenerData ]);
return { matches , isLoading };
}
Benefits Calculator
Shows estimated annual value of matched programs.
function BenefitsCalculator ({ matches } : { matches : MatchResult [] }) {
const likelyMatches = matches . filter ( m => m . eligibility === "likely" );
const monthlyTotal = likelyMatches . reduce (( sum , match ) => {
const monthly = match . program . benefitValueCents
? match . program . benefit_frequency === "annual"
? match . program . benefitValueCents / 12
: match . program . benefitValueCents
: 0 ;
return sum + monthly ;
}, 0 );
const annualTotal = monthlyTotal * 12 ;
return (
< Card >
< CardHeader >
< CardTitle > Estimated Benefits </ CardTitle >
</ CardHeader >
< CardContent >
< div className = "text-3xl font-bold text-emerald-600" >
$ { ( annualTotal / 100 ). toLocaleString () } /year
</ div >
< p className = "text-sm text-stone-500" >
Based on { likelyMatches . length } programs you likely qualify for
</ p >
< div className = "mt-4 space-y-2" >
{ likelyMatches . slice ( 0 , 5 ). map ( match => (
< div key = { match . programId } className = "flex justify-between" >
< span > { match . program . shortName || match . program . name } </ span >
< span className = "text-emerald-600" >
$ { ( match . program . benefitValueCents / 100 ). toLocaleString () }
/ { match . program . benefit_frequency }
</ span >
</ div >
)) }
</ div >
</ CardContent >
</ Card >
);
}
Accessibility
The screener follows WCAG 2.1 AA guidelines:
Keyboard navigation : All steps navigable via Tab/Enter
Screen reader support : ARIA labels on all inputs
Focus management : Focus moves to first input on step change
Error handling : Inline validation with clear error messages
Progress indication : Step progress announced to screen readers
< form
aria-label = "Benefits eligibility screener"
aria-describedby = "screener-description"
>
< div id = "screener-description" className = "sr-only" >
Answer questions about your household to find benefits you may qualify for.
Step { currentStep } of { totalSteps } .
</ div >
{ /* Step content */ }
</ form >
Analytics Events
Track screener usage for optimization.
const SCREENER_EVENTS = {
STARTED: "screener_started" ,
STEP_COMPLETED: "screener_step_completed" ,
STEP_SKIPPED: "screener_step_skipped" ,
COMPLETED: "screener_completed" ,
ABANDONED: "screener_abandoned" ,
PROGRAM_SAVED: "screener_program_saved" ,
};
// Track step completion
analytics . track ( SCREENER_EVENTS . STEP_COMPLETED , {
step: currentStep ,
stepName: STEP_NAMES [ currentStep ],
timeOnStep: Date . now () - stepStartTime ,
matchCount: matches . length ,
});
Next Steps
Eligibility Engine How matching works under the hood
Programs Database Schema for benefit programs