ForgeCheckbox

September 11, 2025 · View on GitHub

Flexible checkbox component with unified SSR/client architecture for binary selections, multi-select forms, and option toggles.

Unified SSR Architecture

The ForgeCheckbox component uses our revolutionary unified wrapper approach - a single component that works everywhere without any developer configuration:

  • SSR Environment (Next.js): Renders semantic HTML <input type="checkbox"> with critical styling
  • Client Environment (Vite): Renders as web component with full interactivity
  • Progressive Enhancement: Seamlessly upgrades from HTML checkbox to web component
  • Graceful Degradation: Falls back to enhanced HTML checkbox if web components fail

Single component that works everywhere - no separate SSR/client versions needed.

Key Features

  • Native Form Integration: Full HTML form submission support
  • Indeterminate State: Three-state checkbox (checked/unchecked/indeterminate)
  • Size Options: Small, medium, and large sizes
  • Custom Styling: Flexible appearance with CSS custom properties
  • Label Integration: Built-in label support with proper accessibility
  • Validation States: Error, success, and warning visual states
  • Accessibility: Complete WCAG 2.1 AA compliance
  • Unified SSR: Works seamlessly in both SSR and client environments
  • AI-Ready: Complete AI metadata for intelligent interactions

Basic Usage

Import (Works in any React environment)

import { ForgeCheckbox } from '@nexcraft/forge/integrations/react';

// Same import works in:
// ✅ Next.js App Router (SSR)
// ✅ Next.js Pages Router (SSR) 
// ✅ Vite (Client-only)
// ✅ CRA (Client-only)
// ✅ Any React SSR framework

Basic Checkboxes

// Simple checkbox
<ForgeCheckbox 
  name="terms" 
  value="accepted"
  onChange={handleChange}
>
  I agree to the terms and conditions
</ForgeCheckbox>

// Pre-checked checkbox
<ForgeCheckbox 
  name="newsletter" 
  checked
  onChange={handleNewsletterChange}
>
  Subscribe to newsletter
</ForgeCheckbox>

// Disabled checkbox
<ForgeCheckbox 
  name="readonly" 
  checked 
  disabled
>
  Read-only option
</ForgeCheckbox>

Checkbox with Different States

// Indeterminate state (useful for "select all")
<ForgeCheckbox 
  name="selectAll"
  indeterminate
  onChange={handleSelectAll}
>
  Select All Items
</ForgeCheckbox>

// Error state
<ForgeCheckbox 
  name="required" 
  required
  error
  errorMessage="This field is required"
>
  Required selection
</ForgeCheckbox>

// Success state
<ForgeCheckbox 
  name="verified" 
  checked 
  success
>
  Email verified
</ForgeCheckbox>

Advanced Usage

Form Integration

function SettingsForm() {
  const [settings, setSettings] = useState({
    notifications: true,
    marketing: false,
    updates: false
  });
  
  const handleSettingChange = (name, checked) => {
    setSettings(prev => ({
      ...prev,
      [name]: checked
    }));
  };
  
  return (
    <form>
      <fieldset>
        <legend>Email Preferences</legend>
        
        <ForgeCheckbox
          name="notifications"
          checked={settings.notifications}
          onChange={(e) => handleSettingChange('notifications', e.target.checked)}
        >
          Receive notifications
        </ForgeCheckbox>
        
        <ForgeCheckbox
          name="marketing"
          checked={settings.marketing}
          onChange={(e) => handleSettingChange('marketing', e.target.checked)}
        >
          Marketing emails
        </ForgeCheckbox>
        
        <ForgeCheckbox
          name="updates"
          checked={settings.updates}
          onChange={(e) => handleSettingChange('updates', e.target.checked)}
        >
          Product updates
        </ForgeCheckbox>
      </fieldset>
      
      <ForgeButton type="submit">Save Settings</ForgeButton>
    </form>
  );
}

Multi-Select with Select All

function MultiSelectList({ items, onSelectionChange }) {
  const [selectedItems, setSelectedItems] = useState(new Set());
  
  const allSelected = selectedItems.size === items.length;
  const someSelected = selectedItems.size > 0 && selectedItems.size < items.length;
  
  const handleSelectAll = (checked) => {
    if (checked) {
      setSelectedItems(new Set(items.map(item => item.id)));
    } else {
      setSelectedItems(new Set());
    }
  };
  
  const handleItemSelect = (itemId, checked) => {
    const newSelection = new Set(selectedItems);
    if (checked) {
      newSelection.add(itemId);
    } else {
      newSelection.delete(itemId);
    }
    setSelectedItems(newSelection);
    onSelectionChange(Array.from(newSelection));
  };
  
  return (
    <div className="multi-select-list">
      <ForgeCheckbox
        indeterminate={someSelected}
        checked={allSelected}
        onChange={(e) => handleSelectAll(e.target.checked)}
        className="select-all-checkbox"
      >
        Select All ({selectedItems.size} of {items.length})
      </ForgeCheckbox>
      
      <hr />
      
      {items.map(item => (
        <ForgeCheckbox
          key={item.id}
          checked={selectedItems.has(item.id)}
          onChange={(e) => handleItemSelect(item.id, e.target.checked)}
        >
          {item.name}
        </ForgeCheckbox>
      ))}
    </div>
  );
}

Properties

PropertyTypeDefaultDescription
namestringundefinedForm field name
valuestringundefinedForm field value
checkedbooleanfalseWhether checkbox is checked
indeterminatebooleanfalseShow indeterminate state
disabledbooleanfalseDisable checkbox interactions
requiredbooleanfalseMark as required field
size'sm' | 'md' | 'lg''md'Checkbox size
errorbooleanfalseShow error state
successbooleanfalseShow success state
warningbooleanfalseShow warning state
errorMessagestringundefinedError message text
helpTextstringundefinedHelp text description

Events

EventDetailDescription
change{ checked: boolean, value: string }Fired when checkbox state changes
focusFocusEventFired when checkbox receives focus
blurFocusEventFired when checkbox loses focus

Methods

MethodParametersReturnsDescription
toggle()-voidToggle checkbox state
check()-voidSet checkbox to checked
uncheck()-voidSet checkbox to unchecked
setIndeterminate()-voidSet to indeterminate state
focus(options?)FocusOptionsvoidFocus the checkbox

SSR Fallback Behavior

In SSR environments, ForgeCheckbox renders a semantic HTML checkbox that provides full functionality even without JavaScript:

Server-Side Rendering

<!-- Checkbox rendered during SSR -->
<label class="forge-checkbox">
  <input 
    type="checkbox" 
    name="newsletter" 
    value="yes"
    class="forge-checkbox__input"
  />
  <span class="forge-checkbox__checkmark" aria-hidden="true"></span>
  <span class="forge-checkbox__label">Subscribe to newsletter</span>
</label>

<!-- Required checkbox with error state -->
<label class="forge-checkbox forge-checkbox--error">
  <input 
    type="checkbox" 
    name="terms" 
    required
    class="forge-checkbox__input"
    aria-describedby="terms-error"
  />
  <span class="forge-checkbox__checkmark" aria-hidden="true"></span>
  <span class="forge-checkbox__label">I agree to terms</span>
  <span id="terms-error" class="forge-checkbox__error">This field is required</span>
</label>

Client-Side Hydration

<!-- After hydration to web component -->
<forge-checkbox name="newsletter" value="yes">
  Subscribe to newsletter
</forge-checkbox>

<forge-checkbox name="terms" required error error-message="This field is required">
  I agree to terms
</forge-checkbox>

Accessibility

WCAG 2.1 AA Compliance

  • Keyboard Navigation: Full Tab, Space key support
  • Focus Management: Visible focus indicators
  • Screen Reader Support: Proper labels and state announcements
  • Form Integration: Native form submission and validation
  • Error Handling: Accessible error message association
  • Required Fields: Proper required field indication

ARIA Support

<ForgeCheckbox
  name="consent"
  required
  aria-describedby="consent-help consent-error"
  error={hasError}
  errorMessage="Consent is required to proceed"
>
  I consent to data processing
</ForgeCheckbox>

<div id="consent-help" className="help-text">
  We need your consent to process your personal data
</div>

Keyboard Support

KeyAction
TabFocus checkbox
Shift + TabFocus previous element
SpaceToggle checkbox state
EnterSubmit form (if in form)

Framework Integration

Next.js App Router (SSR)

// app/components/ConsentForm.tsx - Automatic SSR
import { ForgeCheckbox } from '@nexcraft/forge/integrations/react';

export default function ConsentForm() {
  return (
    <form>
      <ForgeCheckbox name="terms" required>
        I agree to the Terms of Service
      </ForgeCheckbox>
      
      <ForgeCheckbox name="privacy" required>
        I agree to the Privacy Policy
      </ForgeCheckbox>
      
      <ForgeCheckbox name="marketing">
        I want to receive marketing emails (optional)
      </ForgeCheckbox>
      
      <ForgeButton type="submit">Continue</ForgeButton>
    </form>
  );
}

Vite/CRA (Client-only)

// Same component, automatically renders as web component
import { ForgeCheckbox } from '@nexcraft/forge/integrations/react';

function PreferencesPanel() {
  const [preferences, setPreferences] = useState({
    darkMode: false,
    notifications: true,
    autoSave: true
  });
  
  const handlePreferenceChange = (key, value) => {
    setPreferences(prev => ({ ...prev, [key]: value }));
  };
  
  return (
    <div className="preferences-panel">
      <h3>Preferences</h3>
      
      <ForgeCheckbox
        checked={preferences.darkMode}
        onChange={(e) => handlePreferenceChange('darkMode', e.target.checked)}
      >
        Dark mode
      </ForgeCheckbox>
      
      <ForgeCheckbox
        checked={preferences.notifications}
        onChange={(e) => handlePreferenceChange('notifications', e.target.checked)}
      >
        Enable notifications
      </ForgeCheckbox>
      
      <ForgeCheckbox
        checked={preferences.autoSave}
        onChange={(e) => handlePreferenceChange('autoSave', e.target.checked)}
      >
        Auto-save changes
      </ForgeCheckbox>
    </div>
  );
}

Examples

Todo List with Select All

function TodoList({ todos, onTodoChange, onSelectAll }) {
  const completedTodos = todos.filter(todo => todo.completed);
  const allCompleted = completedTodos.length === todos.length;
  const someCompleted = completedTodos.length > 0 && completedTodos.length < todos.length;
  
  return (
    <div className="todo-list">
      <ForgeCheckbox
        indeterminate={someCompleted}
        checked={allCompleted}
        onChange={(e) => onSelectAll(e.target.checked)}
        className="select-all"
      >
        {someCompleted 
          ? `${completedTodos.length} of ${todos.length} completed`
          : allCompleted 
            ? 'All completed'
            : 'Mark all complete'
        }
      </ForgeCheckbox>
      
      {todos.map(todo => (
        <ForgeCheckbox
          key={todo.id}
          checked={todo.completed}
          onChange={(e) => onTodoChange(todo.id, e.target.checked)}
        >
          {todo.text}
        </ForgeCheckbox>
      ))}
    </div>
  );
}

Permission Settings

function PermissionSettings({ permissions, onChange }) {
  const handlePermissionChange = (permission, granted) => {
    onChange(permission, granted);
  };
  
  return (
    <div className="permission-settings">
      <h3>App Permissions</h3>
      
      <ForgeCheckbox
        checked={permissions.camera}
        onChange={(e) => handlePermissionChange('camera', e.target.checked)}
        size="lg"
      >
        <div className="permission-item">
          <ForgeIcon name="camera" />
          <div>
            <strong>Camera Access</strong>
            <p>Allow app to access your camera for photos</p>
          </div>
        </div>
      </ForgeCheckbox>
      
      <ForgeCheckbox
        checked={permissions.location}
        onChange={(e) => handlePermissionChange('location', e.target.checked)}
        size="lg"
      >
        <div className="permission-item">
          <ForgeIcon name="map-pin" />
          <div>
            <strong>Location Access</strong>
            <p>Allow app to access your location</p>
          </div>
        </div>
      </ForgeCheckbox>
      
      <ForgeCheckbox
        checked={permissions.notifications}
        onChange={(e) => handlePermissionChange('notifications', e.target.checked)}
        size="lg"
      >
        <div className="permission-item">
          <ForgeIcon name="bell" />
          <div>
            <strong>Push Notifications</strong>
            <p>Receive notifications about important updates</p>
          </div>
        </div>
      </ForgeCheckbox>
    </div>
  );
}

CSS Custom Properties

forge-checkbox {
  /* Base styles */
  --checkbox-size: 20px;
  --checkbox-border-width: 2px;
  --checkbox-border-radius: 4px;
  --checkbox-transition: all 0.2s ease;
  --checkbox-focus-ring: 0 0 0 3px rgba(59, 130, 246, 0.1);
  
  /* Size variants */
  --checkbox-sm-size: 16px;
  --checkbox-md-size: 20px;
  --checkbox-lg-size: 24px;
  
  /* Colors */
  --checkbox-border-color: var(--forge-color-neutral-300);
  --checkbox-bg-color: white;
  --checkbox-checked-bg: var(--forge-color-primary-500);
  --checkbox-checked-border: var(--forge-color-primary-500);
  --checkbox-checkmark-color: white;
  
  /* Hover states */
  --checkbox-hover-border: var(--forge-color-neutral-400);
  --checkbox-hover-bg: var(--forge-color-neutral-50);
  
  /* Disabled state */
  --checkbox-disabled-opacity: 0.5;
  --checkbox-disabled-cursor: not-allowed;
  
  /* Error state */
  --checkbox-error-border: var(--forge-color-error-500);
  --checkbox-error-bg: var(--forge-color-error-50);
  --checkbox-error-text: var(--forge-color-error-700);
  
  /* Success state */
  --checkbox-success-border: var(--forge-color-success-500);
  --checkbox-success-bg: var(--forge-color-success-50);
  --checkbox-success-text: var(--forge-color-success-700);
  
  /* Label styling */
  --checkbox-label-font-size: 14px;
  --checkbox-label-color: var(--forge-color-neutral-700);
  --checkbox-label-spacing: 8px;
  
  /* Indeterminate state */
  --checkbox-indeterminate-bg: var(--forge-color-primary-500);
  --checkbox-indeterminate-mark-color: white;
}

AI Integration

State Explanation

const checkbox = document.querySelector('forge-checkbox');
console.log(checkbox.explainState());
// "Checkbox is unchecked, enabled for user interaction, not required"

Possible Actions

const actions = checkbox.getPossibleActions();
// [
//   { name: 'toggle', available: true, description: 'Toggle checkbox state' },
//   { name: 'check', available: true, description: 'Mark checkbox as checked' },
//   { name: 'uncheck', available: true, description: 'Mark checkbox as unchecked' }
// ]

Performance

  • Lightweight: <3KB gzipped including styles
  • SSR Optimized: Native HTML form integration with minimal hydration
  • Memory Efficient: Automatic cleanup of event listeners
  • Accessible by Default: No additional ARIA configuration needed

Browser Support

  • Modern Browsers: Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
  • SSR Fallback: Works in any browser with HTML form support
  • Progressive Enhancement: Full functionality without JavaScript in SSR