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-visibleguarantees keyboard users can locate and operate elements without mouse dependency - Visual consistency — Uniform border widths and spacing across states prevent layout shifts
- Action feedback —
:activeand 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:
- Load page — confirm Default renders correctly
- Hover with mouse — verify Hover feedback appears
- Tab to element — Focus-Visible ring must be clearly visible
- Click and hold — Active/Pressed state activates immediately
- Set
disabledattribute — Disabled styling applies and blocks interaction - Add
data-state="error"— Error visual treatment displays - Add
data-state="loading"— Loading indicator replaces content - 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.mdand enforced as a hard requirement, not a suggestion - Implementation uses semantic HTML, CSS custom properties, and
data-stateattributes for JavaScript control - Missing states—particularly
:focus-visibleor: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.
Are the eight states required for non-button elements like inputs and links?
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →