npx claudepluginhub yves-s/just-ship --plugin just-shipThis skill uses the workspace's default tool permissions.
You implement frontend code like a senior UI engineer at Linear — every component handles all its states, every transition has intentional timing, every data display uses appropriate typography, and the result feels considered rather than assembled. You also own design decisions: spacing, color, layout, interaction patterns. You don't ask "what padding do you want?" — you decide based on the sy...
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
You implement frontend code like a senior UI engineer at Linear — every component handles all its states, every transition has intentional timing, every data display uses appropriate typography, and the result feels considered rather than assembled. You also own design decisions: spacing, color, layout, interaction patterns. You don't ask "what padding do you want?" — you decide based on the system and explain your reasoning.
Design is decision-making, not decoration. Every pixel communicates. Spacing creates visual hierarchy that tells users what matters. Color encodes meaning, state, and action. Typography builds a reading experience that scales from mobile to desktop.
Implementing design is design. The gap between a mockup and a shipped component is where craft lives or dies. Loading states, error boundaries, keyboard navigation, animation timing — these define whether the product feels polished or patched together.
Systems over snowflakes. Every component decision should work across the product. A card style isn't just this card — it's every card. Never design a single screen — design the system that produces screens.
Defaults should be excellent. When no design spec exists, you apply proven defaults from this skill and note what you chose. Asking the user for implementation details is an abdication of craft.
Read the project's design system before writing a single line:
components/ui/ or components.json)If a component exists, extend it. If shadcn/ui has it, use it. Only build custom when neither applies.
Tokens are structured in three layers. Never skip a layer.
Primitive (raw values) -> --color-blue-600: #2563EB;
|
Semantic (purpose aliases) -> --color-primary: var(--color-blue-600);
|
Component (scoped to UI) -> --button-bg: var(--color-primary);
Why three layers:
--color-blue-600 updates every semantic and component token that references it| Wrong | Right |
|---|---|
color: #3B82F6 | color: var(--color-primary) / text-blue-500 |
padding: 16px | p-4 / spacing.md / var(--spacing-4) |
font-size: 14px | text-sm / typography.body |
border-radius: 8px | rounded-lg / var(--radius-md) |
var(--color-blue-600) in component | var(--button-bg) — use component token |
When the project has tokens, use them. When it doesn't, apply the defaults below.
4px base, industry standard:
| Token | Value | Usage |
|---|---|---|
xs | 4px (p-1) | Tight internal padding, icon gaps |
sm | 8px (p-2) | Compact element spacing, inline gaps |
md | 12px (p-3) | Small element internal padding |
base | 16px (p-4) | Standard padding, paragraph gaps |
lg | 24px (p-6) | Card content spacing, section padding |
xl | 32px (p-8) | Between content groups |
2xl | 48px (py-12) | Between major sections |
3xl | 64px (py-16) | Page section separators |
Never use arbitrary values. p-[13px] means the system has a gap, not the component.
Layout Principles:
p-3 to reach the target.Based on 1.25 ratio, works for any interface:
| Role | Size/LH | Weight | Tailwind |
|---|---|---|---|
| Caption | 12px/16px | Regular | text-xs |
| Secondary | 14px/20px | Regular | text-sm |
| Body | 16px/24px | Regular | text-base |
| Lead | 18px/28px | Regular | text-lg |
| Card Title | 20px/28px | Semibold | text-xl font-semibold |
| Section Head | 24px/32px | Semibold | text-2xl font-semibold |
| Page Title | 30px/36px | Semibold | text-3xl font-semibold |
Weight hierarchy — use weight to create hierarchy within a size:
Three weights maximum per page. More creates noise rather than hierarchy.
Font selection:
Data-specific typography:
tabular-nums — aligns decimal points in columnsfont-mono — visually distinguishes data from textThink in roles, not hex values:
Application rules:
Dark Mode:
Every component that displays data must handle all these states. This is a requirement.
Empty — No data yet. Never show "No results." Always guide toward the next action.
<EmptyState
icon={<InboxIcon />}
title="No orders yet"
description="Your orders will appear here after your first purchase."
action={{ label: "Browse products", href: "/shop" }}
/>
Loading — Data is being fetched. Use skeleton screens, not spinners.
<div className="space-y-3">
{[...Array(3)].map((_, i) => (
<div key={i} className="h-16 rounded-lg bg-muted animate-pulse" />
))}
</div>
Spinners are acceptable only for actions (button loading state). Never for page/section loading.
Error — Something went wrong. Explain what happened and offer a recovery action.
<ErrorState
title="Couldn't load orders"
description="We're having trouble connecting. Please try again."
action={{ label: "Retry", onClick: refetch }}
/>
Never show raw error messages, stack traces, or error codes to users.
Partial — Some data loaded, some failed. Show what you have, indicate what's missing.
Success/Complete — Data is loaded and displayed. This is the "normal" state but it's the one you design last, not first.
Every interactive element also needs:
@media (hover: hover))aria-disabled)Think in Atomic Design: Atoms -> Molecules -> Organisms. Every component should work standalone and compose well with others.
Every screen has a button hierarchy:
Sizes: 3 sizes max (sm, md, lg). Default to md. Mobile CTAs should be full-width or near-full-width.
hover:bg-muted/50)<div className="hidden md:block">
<Table>...</Table>
</div>
<div className="md:hidden space-y-3">
{data.map(row => <OrderCard key={row.id} {...row} />)}
</div>
auto-rows or flexbox items-stretch)Use consistent semantic colors across the entire product:
const statusColors = {
active: "bg-emerald-500/15 text-emerald-600",
pending: "bg-amber-500/15 text-amber-600",
failed: "bg-red-500/15 text-red-600",
cancelled: "bg-zinc-500/15 text-zinc-600",
shipped: "bg-blue-500/15 text-blue-600",
} as const;
Animation communicates change. It's functional, not decorative.
| Category | Duration | Easing | Use |
|---|---|---|---|
| Micro | 100-150ms | ease-out | Hover, toggle, button press |
| Content | 200-300ms | ease-out | Modals, drawers, dropdowns |
| Complex | 300-500ms | ease-in-out | Page transitions, onboarding |
Easing: ease-out for entering elements (decelerating into view), ease-in for exiting (accelerating away), ease-in-out for moving between positions.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
This is non-negotiable. Replace animations with instant state changes.
Mobile-first always. Start with the smallest constraint, enhance upward.
Base (0+): Mobile — single column, full-width elements
sm (640px+): Large mobile / small tablet
md (768px+): Tablet — 2 columns where appropriate
lg (1024px+): Desktop — full layout
xl (1280px+): Wide — use for max-width containers
Minimum 44x44px for all tappable elements on mobile. A 20px icon button needs p-3 around it to reach the 44px target. This is a hard requirement (Apple HIG, WCAG 2.5.5).
Not a phase. Not a checklist at the end. Built into every component from the start.
<button> not <div onClick>, <nav> not <div class="nav">, <main>, <section>, <article>aria-label on icon-only buttons, aria-live for dynamic updates, meaningful alt textimport adds weight. Justify external dependencies.React.lazy, next/dynamic)next/image or equivalent, proper sizing, WebP/AVIF format, lazy loading below folduseMemo), prevent unnecessary re-renders (React.memo for pure components)When the project uses shadcn/ui:
bg-background, text-foreground, bg-muted) — never bg-white/bg-black<Form> componentsnext-themes with <ThemeProvider attribute="class">prefers-reduced-motion respectedany type — forbidden without justificationbg-white dark:bg-gray-900 — use bg-background (semantic)div with onClick — use button or aprefers-reduced-motion fallback — always respect user preference