The 8 Essential States for Interactive Components in Hallmark

Hallmark requires every interactive element to implement eight distinct UI states—Default, Hover, Focus-Visible, Active/Pressed, Disabled, Error, Loading, and Filled/Success—to ensure consistent, accessible, and fully functional interfaces.

Hallmark is an open-source design system created by Nutlope that enforces strict UI quality standards. According to the project's source files, particularly interaction-and-states.md, all eight states must be present on every interactive component. Missing any state—especially :focus-visible, :active, or :disabled—is explicitly flagged as a failure mode that degrades user experience.

The Eight Required States Explained

The following table breaks down each state as documented in Hallmark's reference files. These states apply universally to buttons, inputs, links, cards, and any element that users can interact with.

State Purpose Key Implementation Details
Default Baseline appearance Normal look when no interaction occurs; defines the component's core visual identity
Hover Pointer feedback Background shift, elevation change, or subtle color transition on mouse hover
Focus-Visible Keyboard navigation Visible focus ring or outline for keyboard users; must never be suppressed
Active/Pressed Press confirmation Immediate feedback during click/tap—often inset shadows, scale reduction, or color darkening
Disabled Non-interactive state Dimmed opacity, removed pointer events, and clear visual indication of unavailability
Error Failure indication Red border, error icon, accompanying message, and aria-invalid attribute
Loading Progress state Inline spinner, label replacement, or reduced opacity during async operations
Filled/Success Completion state Checkmark, accent color, or stronger border indicating valid input or successful action

Why All Eight States Matter

Hallmark's eight-state rule addresses common failures in AI-generated and hand-coded interfaces. The documentation in skills/hallmark/references/interaction-and-states.md emphasizes four core benefits:

  • Accessibility compliance:focus-visible guarantees keyboard users can locate and operate elements without mouse dependency
  • Visual consistency — Uniform border widths and spacing across states prevent layout shifts
  • Action feedback:active and loading states provide immediate confirmation that user input registered
  • Error clarity — Dedicated error states eliminate ambiguous color-only signals that fail WCAG contrast requirements

Implementing the 8 States in Code

The following examples demonstrate practical implementation using semantic HTML, CSS custom properties, and state-driven JavaScript. These patterns mirror approaches found in Hallmark's component-cookbook.md and site/js/main.js.

Base HTML Structure

<button class="hallmark-btn" data-state="">
  <span class="hallmark-btn__label">Submit</span>
</button>

CSS State Definitions

/* ---- Default ---- */
.hallmark-btn {
  --bg: var(--color-surface);
  --fg: var(--color-on-surface);
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--color-rule-2);
  border-radius: 0.5rem;
  transition: background 150ms ease, box-shadow 150ms ease, transform 100ms ease;
}

/* ---- Hover ---- */
.hallmark-btn:hover {
  background: var(--color-surface-hover);
  box-shadow: var(--elevation-1);
}

/* ---- Focus-Visible ---- */
.hallmark-btn:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* ---- Active / Pressed ---- */
.hallmark-btn:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 4px var(--color-shadow);
}

/* ---- Disabled ---- */
.hallmark-btn:disabled,
.hallmark-btn[data-state="disabled"] {
  opacity: 0.5;
  pointer-events: none;
  cursor: not-allowed;
}

/* ---- Error ---- */
.hallmark-btn[data-state="error"] {
  border-color: var(--color-error);
  background: var(--color-error-bg);
  color: var(--color-error);
}

/* ---- Loading ---- */
.hallmark-btn[data-state="loading"] .hallmark-btn__label {
  visibility: hidden;
}
.hallmark-btn[data-state="loading"]::after {
  content: "";
  position: absolute;
  width: 1rem;
  height: 1rem;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: hallmark-spin 0.8s linear infinite;
}

/* ---- Filled / Success ---- */
.hallmark-btn[data-state="filled"] {
  background: var(--color-success);
  border-color: var(--color-success);
  color: var(--color-on-success);
}

@keyframes hallmark-spin {
  to { transform: rotate(360deg); }
}

JavaScript State Management

const button = document.querySelector('.hallmark-btn');

button.addEventListener('click', async () => {
  // Prevent re-trigger during active operation
  if (button.dataset.state === 'loading') return;

  // 1. Enter loading state
  button.dataset.state = 'loading';
  button.disabled = true;

  try {
    await performAsyncAction();           // Replace with actual operation
    button.dataset.state = 'filled';      // Success / filled state
    
    // Auto-reset after delay
    setTimeout(() => {
      button.dataset.state = '';
      button.disabled = false;
    }, 2000);
    
  } catch (error) {
    button.dataset.state = 'error';       // Error state
    
    // Clear error after user acknowledgment delay
    setTimeout(() => {
      button.dataset.state = '';
      button.disabled = false;
    }, 3000);
  }
});

Key Source Files in Hallmark

These files contain the authoritative specifications and implementation patterns for the eight essential states:

File Location Contains
interaction-and-states.md skills/hallmark/references/ Complete eight-state checklist and rationale
microinteractions.md skills/hallmark/references/ Timing, easing curves, and prefers-reduced-motion handling
component-cookbook.md skills/hallmark/references/ Production-ready component examples
main.js site/js/ Runtime state-toggle logic for the demo site

Verifying State Completeness

To audit a component against Hallmark's requirements, check each state sequentially:

  1. Load page — confirm Default renders correctly
  2. Hover with mouse — verify Hover feedback appears
  3. Tab to element — Focus-Visible ring must be clearly visible
  4. Click and hold — Active/Pressed state activates immediately
  5. Set disabled attribute — Disabled styling applies and blocks interaction
  6. Add data-state="error"Error visual treatment displays
  7. Add data-state="loading"Loading indicator replaces content
  8. Add data-state="filled"Filled/Success completion state shows

All eight must function independently without visual regressions in other states.

Summary

  • Hallmark mandates eight essential states for every interactive component: Default, Hover, Focus-Visible, Active/Pressed, Disabled, Error, Loading, and Filled/Success
  • These states are defined in interaction-and-states.md and enforced as a hard requirement, not a suggestion
  • Implementation uses semantic HTML, CSS custom properties, and data-state attributes for JavaScript control
  • Missing states—particularly :focus-visible or :disabled—constitute explicit failure modes per the Hallmark specification
  • The pattern appears throughout Hallmark's reference files, cookbook examples, and demo site JavaScript

Frequently Asked Questions

What happens if a Hallmark component omits one of the eight states?

Omitting any state violates the design system's core contract. The documentation in interaction-and-states.md treats missing states as failure modes that produce "almost-right" interfaces—broken keyboard navigation, ambiguous feedback, or inaccessible error messaging. Automated or manual audits should flag incomplete state coverage.

Yes. The eight-state rule applies universally to every interactive element. Inputs require the same state coverage plus additional considerations for validation timing. Links implement a subset of visual states but still require :focus-visible, :hover, and :active at minimum.

How does Hallmark handle the Loading state for accessibility?

The loading state must preserve focus management and announce status changes to screen readers. The reference implementation uses aria-live regions or aria-busy attributes, ensures the element remains focusable, and maintains sufficient color contrast for the loading indicator—never relying solely on animation.

Can I customize the visual appearance of each state?

Customization is expected through CSS custom properties (--color-*, --elevation-*), but the existence of each state is non-negotiable. The microinteractions.md file specifies timing constraints (default 150ms transitions) and requires prefers-reduced-motion fallbacks for accessibility compliance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →