AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Frontend Ui Engineering

skill-outlinedriven-odin-claude-plugin-frontend-ui-engineering · by OutlineDriven

Builds production-quality UIs. Use when building or modifying user-facing interfaces. Use when creating components, implementing layouts, managing state, or when the output needs to look and feel production-quality rather than AI-generated.

No reviews yet
0 installs
39 views
0.0% view→install

Install

$ agentstack add skill-outlinedriven-odin-claude-plugin-frontend-ui-engineering

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-outlinedriven-odin-claude-plugin-frontend-ui-engineering)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Frontend Ui Engineering? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Frontend UI Engineering

Overview

Build UIs that read as hand-crafted production work, not machine output. The failure mode is the generic "AI aesthetic": default palettes, oversized cards, template layouts with no tie to the content. Conform to the project's real design system, meet WCAG 2.1 AA, and handle every interaction state.

Examples below pair a JavaScript component framework (React) with a server-rendered template stack (Django/Python); CSS and HTML examples are framework-neutral. The patterns hold across frameworks — apply the equivalent in whatever stack the project uses.

When to Use

  • Building new UI components or pages
  • Modifying existing user-facing interfaces
  • Implementing responsive layouts
  • Adding interactivity or state management
  • Fixing visual or UX issues

Component Architecture

File Structure

Colocate everything that changes together with the component. The principle is stack-independent:

# JavaScript component framework (React)
src/components/TaskList/
  TaskList.tsx          # Component implementation
  TaskList.test.tsx     # Tests
  TaskList.stories.tsx  # Stories (if using a component workbench)
  use-task-list.ts      # Custom hook (when state is non-trivial)
  types.ts              # Component-local types
# Server-rendered templates (Django)
app/components/task_list/
  task_list.html        # Template — presentation
  task_list.py          # View / component logic
  task_list_test.py     # Tests
  task_list.css         # Component-local styles

Component Patterns

Prefer composition over configuration. Expose regions as slots/children, not a wall of props.

// React — composable

  
    Tasks
  
  
    
  

// React — over-configured (avoid)
}
/>
{# Django — composable: a card shell that yields named regions #}
{% component "card" %}
  {% fill "header" %}Tasks{% endfill %}
  {% fill "body" %}{% include "task_list.html" with tasks=tasks %}{% endfill %}
{% endcomponent %}

{# Django — over-configured (avoid) #}
{% component "card" title="Tasks" header_variant="large" body_padding="md" content=task_list %}

Keep components focused. One responsibility per component.

// React — renders a single task row, nothing else
export function TaskItem({ task, onToggle, onDelete }: TaskItemProps) {
  return (
    
       onToggle(task.id)} />
      {task.title}
       onDelete(task.id)}>
        
      
    
  );
}
{# Django — task_item.html: renders a single task row, nothing else #}

  
  {{ task.title }}
  
    {% include "icons/trash.svg" %}
  

Separate data fetching from presentation. The data owner picks the state to render (loading, error, empty, populated); the presentation component only renders.

// React — container owns data
export function TaskListContainer() {
  const { tasks, isLoading, error, refetch } = useTasks();

  if (isLoading) return ;
  if (error) return ;
  if (tasks.length === 0) return ;

  return ;
}

// React — presentation owns rendering
export function TaskList({ tasks }: { tasks: Task[] }) {
  return (
    
      {tasks.map(task => )}
    
  );
}
# Django — the view owns data and chooses which state to render (MVT)
def task_list(request):
    try:
        tasks = Task.objects.for_user(request.user)
    except DataError:
        return render(request, "errors/load_failed.html",
                      {"retry_url": request.path}, status=503)
    template = "task_list.html" if tasks else "empty_state.html"
    return render(request, template, {"tasks": tasks})
{# Django — task_list.html owns rendering only #}

  {% for task in tasks %}{% include "task_item.html" with task=task %}{% endfor %}

State Management

Pick the narrowest scope that holds the state. Categories, narrowest to widest:

Local state    → component-specific UI state           React: useState · Vue: ref/reactive
Lifted state   → shared between 2-3 components in the same hierarchy  props + change handlers
Context        → theme, auth, locale (read-heavy,       React: Context · Vue: provide/inject
                 write-rare)
URL state      → filters, pagination, shareable UI      router/searchParams (any stack)
                 state
Server state   → remote data with caching              React Query, SWR; Vue: TanStack Query
Global store   → complex client state shared app-wide   Zustand, Redux; Vue: Pinia

Server-rendered stacks map the same categories onto request/session/query-param/database state — the categories hold; only the storage moves to the server.

Avoid prop drilling deeper than 3 levels. If you pass props through components that don't use them, introduce context or restructure the component tree.

Design System Adherence

Avoid the AI Aesthetic

Machine-generated UI has recognizable tells. Reject each:

| AI Default | Why It Is a Problem | Production Quality | |---|---|---| | Purple/indigo everything | Models default to visually "safe" palettes, making every app look identical | Use the project's actual color palette | | Excessive gradients | Gradients add visual noise and clash with most design systems | Flat or subtle gradients matching the design system | | Rounded everything (rounded-2xl) | Maximum rounding signals "friendly" but ignores the hierarchy of corner radii in real designs | Consistent border-radius from the design system | | Generic hero sections | Template-driven layout with no connection to the actual content or user need | Content-first layouts | | Lorem ipsum-style copy | Placeholder text hides layout problems that real content reveals (length, wrapping, overflow) | Realistic placeholder content | | Oversized padding everywhere | Equal generous padding destroys visual hierarchy and wastes screen space | Consistent spacing scale | | Stock card grids | Uniform grids are a layout shortcut that ignores information priority and scanning patterns | Purpose-driven layouts | | Shadow-heavy design | Layered shadows add depth that competes with content and slows rendering on low-end devices | Subtle or no shadows unless the design system specifies |

Spacing and Layout

Use the project's spacing scale. Do not invent off-scale values:

/* Use the scale: 0.25rem increments (or whatever the project uses) */
/* Good */  padding: 1rem;      /* 16px */
/* Good */  gap: 0.75rem;       /* 12px */
/* Bad */   padding: 13px;      /* Not on any scale */
/* Bad */   margin-top: 2.3rem; /* Not on any scale */

Typography

Keep the heading hierarchy:

h1 → Page title (one per page)
h2 → Section title
h3 → Subsection title
body → Default text
small → Secondary/helper text

Do not skip heading levels. Do not borrow heading styles for non-heading content.

Color

  • Use semantic color tokens: text-primary, bg-surface, border-default — not raw hex values
  • Ensure sufficient contrast (4.5:1 for normal text, 3:1 for large text)
  • Do not rely solely on color to convey information (use icons, text, or patterns too)

Accessibility (WCAG 2.1 AA)

Every component meets these standards. Use the native element first; reach for ARIA only when no native element fits.

Keyboard Navigation

The native element is focusable and operable in any stack:

Click me          
Click me   

When a custom widget is unavoidable, restore the keyboard contract. Add tabindex, a role, and the Enter/Space handling the native control gives for free:

// React
 {
       if (e.key === 'Enter') handleClick();
       if (e.key === ' ') e.preventDefault();
     }}
     onKeyUp={e => {
       if (e.key === ' ') handleClick();
     }}>
  Click me

Click me

  const el = document.getElementById('action');
  el.addEventListener('click', handleClick);
  el.addEventListener('keydown', e => {
    if (e.key === 'Enter') handleClick();
    if (e.key === ' ') e.preventDefault();
  });
  el.addEventListener('keyup', e => { if (e.key === ' ') handleClick(); });

ARIA Labels

Label every interactive element that lacks visible text. The markup is stack-neutral (React uses htmlFor where plain HTML uses for):


Email

Focus Management

Move focus when content appears, and trap it inside modal surfaces until they close:

// React
function Dialog({ isOpen, onClose }: DialogProps) {
  const closeRef = useRef(null);

  useEffect(() => {
    if (isOpen) closeRef.current?.focus();
  }, [isOpen]);

  // Trap focus inside the dialog while open
  return (
    
      Close
      {/* dialog content */}
    
  );
}

  Close
  

  function openDialog() {
    const dialog = document.getElementById('confirm');
    dialog.showModal();                                  // native modal traps focus
    document.getElementById('confirm-close').focus();    // move focus in
  }

Meaningful Empty and Error States

Never render a blank screen. Give the empty state a purpose and a next action:

// React
function TaskList({ tasks }: { tasks: Task[] }) {
  if (tasks.length === 0) {
    return (
      
        
        No tasks
        Get started by creating a new task.
        Create Task
      
    );
  }

  return ...;
}
{# Django — empty_state.html rendered when the view finds no tasks #}

  {% include "icons/tasks_empty.svg" %}
  No tasks
  Get started by creating a new task.
  Create Task

Responsive Design

Build mobile-first, then widen. Express breakpoints with utility classes or plain CSS — both are framework-neutral:

/* Plain CSS, mobile-first */
.grid { display: grid; grid-template-columns: 1fr; gap: 1rem; }
@media (min-width: 768px)  { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } }

Test at these breakpoints: 320px, 768px, 1024px, 1440px.

Loading and Transitions

Use skeletons for content, not spinners. The skeleton markup is stack-neutral:


  
  
  

Apply optimistic updates so the UI responds before the server confirms. Cache mutation is a client-side pattern; the shape is identical in React Query and Vue's TanStack Query — snapshot, apply, roll back on error:

// React (TanStack Query)
function useToggleTask() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: toggleTask,
    onMutate: async (taskId) => {
      await queryClient.cancelQueries({ queryKey: ['tasks'] });
      const previous = queryClient.getQueryData(['tasks']);

      queryClient.setQueryData(['tasks'], (old: Task[]) =>
        old.map(t => t.id === taskId ? { ...t, done: !t.done } : t)
      );

      return { previous };
    },
    onError: (_err, _taskId, context) => {
      queryClient.setQueryData(['tasks'], context?.previous);
    },
  });
}
// Vue (@tanstack/vue-query) — same snapshot/apply/rollback contract
function useToggleTask() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: toggleTask,
    onMutate: async (taskId) => {
      await queryClient.cancelQueries({ queryKey: ['tasks'] });
      const previous = queryClient.getQueryData(['tasks']);
      queryClient.setQueryData(['tasks'], (old) =>
        old.map(t => t.id === taskId ? { ...t, done: !t.done } : t)
      );
      return { previous };
    },
    onError: (_err, _taskId, context) => {
      queryClient.setQueryData(['tasks'], context.previous);
    },
  });
}

See Also

Detailed WCAG checks and testing tools are in references/accessibility-checklist.md.

Common Rationalizations

| Rationalization | Reality | |---|---| | "Accessibility is a nice-to-have" | It's a legal requirement in many jurisdictions and an engineering quality standard. | | "We'll make it responsive later" | Retrofitting responsive design is 3x harder than building it from the start. | | "The design isn't final, so I'll skip styling" | Use the design system defaults. Unstyled UI creates a broken first impression for reviewers. | | "This is just a prototype" | Prototypes become production code. Build the foundation right. | | "The AI aesthetic is fine for now" | It signals low quality. Use the project's actual design system from the start. |

Red Flags

  • Components with more than 200 lines (split them)
  • Inline styles or arbitrary pixel values
  • Missing error states, loading states, or empty states
  • No keyboard navigation testing
  • Color as the sole indicator of state (red/green without text or icons)
  • Generic "AI look" (purple gradients, oversized cards, stock layouts)

Verification

After building UI:

  • [ ] Component renders without console errors
  • [ ] All interactive elements are keyboard accessible (Tab through the page)
  • [ ] Screen reader can convey the page's content and structure
  • [ ] Responsive: works at 320px, 768px, 1024px, 1440px
  • [ ] Loading, error, and empty states all handled
  • [ ] Follows the project's design system (spacing, colors, typography)
  • [ ] No accessibility warnings in dev tools or axe-core

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.