# How to Handle State Management Within Skills in Nutlope/hallmark

> Learn how Hallmark handles state management within skills using declarative CSS, lightweight data attributes, and canonical interaction states for efficient UI updates.

- Repository: [Hassan El Mghari/hallmark](https://github.com/Nutlope/hallmark)
- Tags: how-to-guide
- Published: 2026-07-29

---

**Hallmark handles state management within skills through declarative CSS-driven UI states and lightweight data attributes, keeping business logic outside the skill while supporting eight canonical interaction states and optimistic updates.**

The Nutlope/hallmark repository defines a *skill* as a pure visual and interaction layer that remains framework-agnostic. When you handle state management within skills in Nutlope/hallmark, you work with browser-native patterns rather than importing heavyweight state libraries, ensuring components remain self-contained and portable across themes.

## The Philosophy Behind Hallmark Skills

Hallmark enforces strict boundaries between visual presentation and application logic. According to [`skills/hallmark/references/contract.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/contract.md), the skill contract explicitly states that "state management, data fetching, business rules" are handled outside the skill; the skill only needs to react to the resulting DOM state. This architecture prevents skills from becoming tightly coupled to specific data sources or business logic, making them truly reusable.

### Eight Canonical UI States

Every interactive element in a Hallmark skill must implement a complete set of eight standardized states defined in [`skills/hallmark/references/interaction-and-states.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/interaction-and-states.md):

- **Default**: The resting state of the component
- **Hover**: Mouse interaction (only on devices supporting hover)
- **Focus**: Keyboard navigation via `:focus-visible`
- **Active/Pressed**: The moment of interaction using `:active`
- **Disabled**: Non-interactive state via `[disabled]` or `aria-disabled`
- **Loading**: Async operation in progress using `[data-state="loading"]`
- **Error**: Failure state triggered by validation or network errors
- **Success**: Completion feedback such as `[data-state="copied"]`

## Implementing State-Driven UI

State management relies on CSS custom properties and attribute selectors rather than JavaScript class manipulation. This approach minimizes JavaScript execution and leverages the browser's rendering engine for smooth transitions.

### Using Data Attributes for Transient States

JavaScript only toggles `data-state` attributes to communicate transient UI flags. CSS reacts to these attributes to provide visual feedback, keeping the DOM as the single source of truth.

```javascript
// Copy-to-clipboard microinteraction (uses data-state)
const copyBtn = document.querySelector('.copy-btn');
copyBtn.addEventListener('click', async () => {
  const text = copyBtn.dataset.value;
  await navigator.clipboard.writeText(text);
  copyBtn.dataset.state = 'copied';
  setTimeout(() => delete copyBtn.dataset.state, 2500);
});

```

The corresponding CSS in [`skills/hallmark/references/microinteractions.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/microinteractions.md) defines the visual response without requiring additional JavaScript logic:

```css
/* Loading state via data attribute */
.btn[data-state="loading"] {
  cursor: wait;
}
.btn[data-state="loading"]::after {
  content: "";
  display: inline-block;
  width: 1rem; height: 1rem;
  border: 2px solid var(--color-ink);
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

```

### CSS-First State Transitions

Each state is expressed via CSS custom properties or pseudo-classes, avoiding `transition-all` and layout-shifting properties as specified in the microinteractions reference. The implementation reserves space for focus rings to prevent layout shifts:

```css
/* Base styling */
.btn {
  border: 1px solid var(--color-rule-2);
  background: var(--color-paper);
  outline: 2px solid transparent; /* reserve space for focus ring */
  transition:
    background-color var(--dur-short) var(--ease-out),
    transform 100ms var(--ease-out);
}

/* Hover – only for devices that support hover */
@media (hover: hover) {
  .btn:hover { background: var(--color-paper-2); }
}

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

/* Active/pressed */
.btn:active { transform: translateY(1px); }

/* Disabled */
.btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

```

## Handling User Interactions

Complex interactions follow optimistic UI patterns with automatic rollback capabilities, implemented in `site/examples/**/script.js` and documented in [`skills/hallmark/references/microinteractions.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/microinteractions.md).

### Optimistic UI Updates with Rollback

Actions update the UI instantly before firing async requests. If the request fails, the interface rolls back with a short animation and presents an Undo toast via the global utility system defined in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js):

```javascript
// Optimistic toggle with rollback + Undo toast
async function toggleItem(item) {
  const prev = item.completed;
  item.completed = !prev;        // UI updates instantly
  render();                     // re-render the list

  try {
    await api.update(item);      // async persistence
  } catch {
    item.completed = prev;        // rollback UI
    render();
    toast({
      tone: 'error',
      message: "Couldn't save.",
      action: { label: 'Undo', run: () => toggleItem(item) }
    });
  }
}

```

### Accessibility and Reduced Motion

All motion respects `prefers-reduced-motion: reduce` as a first-class state. When the media query matches, animations collapse to instant opacity changes rather than disabling transitions entirely, maintaining usability while respecting user preferences:

```css
@media (prefers-reduced-motion: reduce) {
  .btn {
    transition-duration: 0.01ms;
  }
}

```

## Key Implementation Files

The following files define the complete state-management strategy for Hallmark skills:

- **[`skills/hallmark/references/interaction-and-states.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/interaction-and-states.md)**: Defines the eight required UI states and CSS recipes
- **[`skills/hallmark/references/microinteractions.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/microinteractions.md)**: Provides motion guidelines, reduced-motion handling, and concrete JS/CSS recipes
- **[`skills/hallmark/references/contract.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/contract.md)**: Establishes that skills handle only visual/interaction concerns
- **[`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js)**: Entry point for global UI utilities such as the toast system
- **`site/examples/**/script.js`**: Real-world demonstrations of state toggles and loading patterns

## Summary

- **Hallmark skills avoid custom state machines** and heavyweight frameworks by using declarative CSS and data attributes.
- **Eight canonical states** (default, hover, focus, active, disabled, loading, error, success) must be implemented for every interactive element.
- **JavaScript only toggles `data-state` attributes**; CSS handles all visual transitions and animations.
- **Optimistic updates** provide immediate feedback with automatic rollback and Undo toast support on failure.
- **Business logic lives outside the skill** according to the contract defined in [`skills/hallmark/references/contract.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/contract.md).

## Frequently Asked Questions

### Does Hallmark use React or Redux for state management?

No. Hallmark deliberately avoids complex state-management libraries like Redux or even React hooks within skills. The architecture requires that state management, data fetching, and business rules exist outside the skill component. The skill only reacts to DOM state changes via CSS and minimal JavaScript attribute toggling.

### How do I implement a loading state in a Hallmark skill?

Apply a `data-state="loading"` attribute to the target element via JavaScript. The CSS defined in [`skills/hallmark/references/microinteractions.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/microinteractions.md) responds to this attribute to display visual feedback such as spinners or disabled cursors. Remove the attribute when the operation completes to return to the default or success state.

### What happens if an optimistic update fails?

The UI immediately rolls back to the previous state and renders the original data. Simultaneously, the global toast system (available in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js)) displays an error message with an Undo action, allowing users to retry the operation without manual page refreshes.

### Are animations required for Hallmark skills?

No. Hallmark treats reduced motion as a first-class state. All animations respect the `prefers-reduced-motion: reduce` media query by collapsing to instant opacity changes or 0.01ms transition durations, ensuring accessibility while maintaining functional feedback.