首页/AI 前端工程/web-component-design
W

web-component-design

by @wshobsonv
4.5(22)

探索前端Web组件设计,利用智能自动化和多智能体编排,构建可复用、高性能的模块化界面。

web-componentscustom-elementsshadow-domfrontend-developmentreusable-uiGitHub
安装方式
npx skills add https://github.com/wshobson/agents --skill web-component-design
compare_arrows

Before / After 效果对比

1 组
使用前

传统前端开发中,构建可复用组件往往需要大量手动编码和维护,组件间依赖复杂,性能优化困难,导致开发效率低下且难以适应快速变化的需求。

使用后

采用智能自动化与多智能体编排的Web组件设计,能够自动生成和管理组件,实现高度模块化和高性能界面,极大提升开发效率和组件的可维护性。

SKILL.md

web-component-design

Web Component Design

Build reusable, maintainable UI components using modern frameworks with clean composition patterns and styling approaches.

When to Use This Skill

  • Designing reusable component libraries or design systems

  • Implementing complex component composition patterns

  • Choosing and applying CSS-in-JS solutions

  • Building accessible, responsive UI components

  • Creating consistent component APIs across a codebase

  • Refactoring legacy components into modern patterns

  • Implementing compound components or render props

Core Concepts

1. Component Composition Patterns

Compound Components: Related components that work together

// Usage
<Select value={value} onChange={setValue}>
  <Select.Trigger>Choose option</Select.Trigger>
  <Select.Options>
    <Select.Option value="a">Option A</Select.Option>
    <Select.Option value="b">Option B</Select.Option>
  </Select.Options>
</Select>

Render Props: Delegate rendering to parent

<DataFetcher url="/api/users">
  {({ data, loading, error }) =>
    loading ? <Spinner /> : <UserList users={data} />
  }
</DataFetcher>

Slots (Vue/Svelte): Named content injection points

<template>
  <Card>
    <template #header>Title</template>
    <template #content>Body text</template>
    <template #footer><Button>Action</Button></template>
  </Card>
</template>

2. CSS-in-JS Approaches

Solution Approach Best For

Tailwind CSS Utility classes Rapid prototyping, design systems

CSS Modules Scoped CSS files Existing CSS, gradual adoption

styled-components Template literals React, dynamic styling

Emotion Object/template styles Flexible, SSR-friendly

Vanilla Extract Zero-runtime Performance-critical apps

3. Component API Design

interface ButtonProps {
  variant?: "primary" | "secondary" | "ghost";
  size?: "sm" | "md" | "lg";
  isLoading?: boolean;
  isDisabled?: boolean;
  leftIcon?: React.ReactNode;
  rightIcon?: React.ReactNode;
  children: React.ReactNode;
  onClick?: () => void;
}

Principles:

  • Use semantic prop names (isLoading vs loading)

  • Provide sensible defaults

  • Support composition via children

  • Allow style overrides via className or style

Quick Start: React Component with Tailwind

import { forwardRef, type ComponentPropsWithoutRef } from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        primary: "bg-blue-600 text-white hover:bg-blue-700",
        secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200",
        ghost: "hover:bg-gray-100 hover:text-gray-900",
      },
      size: {
        sm: "h-8 px-3 text-sm",
        md: "h-10 px-4 text-sm",
        lg: "h-12 px-6 text-base",
      },
    },
    defaultVariants: {
      variant: "primary",
      size: "md",
    },
  },
);

interface ButtonProps
  extends
    ComponentPropsWithoutRef<"button">,
    VariantProps<typeof buttonVariants> {
  isLoading?: boolean;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, isLoading, children, ...props }, ref) => (
    <button
      ref={ref}
      className={cn(buttonVariants({ variant, size }), className)}
      disabled={isLoading || props.disabled}
      {...props}
    >
      {isLoading && <Spinner className="mr-2 h-4 w-4" />}
      {children}
    </button>
  ),
);
Button.displayName = "Button";

Framework Patterns

React: Compound Components

import { createContext, useContext, useState, type ReactNode } from "react";

interface AccordionContextValue {
  openItems: Set<string>;
  toggle: (id: string) => void;
}

const AccordionContext = createContext<AccordionContextValue | null>(null);

function useAccordion() {
  const context = useContext(AccordionContext);
  if (!context) throw new Error("Must be used within Accordion");
  return context;
}

export function Accordion({ children }: { children: ReactNode }) {
  const [openItems, setOpenItems] = useState<Set<string>>(new Set());

  const toggle = (id: string) => {
    setOpenItems((prev) => {
      const next = new Set(prev);
      next.has(id) ? next.delete(id) : next.add(id);
      return next;
    });
  };

  return (
    <AccordionContext.Provider value={{ openItems, toggle }}>
      <div className="divide-y">{children}</div>
    </AccordionContext.Provider>
  );
}

Accordion.Item = function AccordionItem({
  id,
  title,
  children,
}: {
  id: string;
  title: string;
  children: ReactNode;
}) {
  const { openItems, toggle } = useAccordion();
  const isOpen = openItems.has(id);

  return (
    <div>
      <button onClick={() => toggle(id)} className="w-full text-left py-3">
        {title}
      </button>
      {isOpen && <div className="pb-3">{children}</div>}
    </div>
  );
};

Vue 3: Composables

<script setup lang="ts">
import { ref, computed, provide, inject, type InjectionKey } from "vue";

interface TabsContext {
  activeTab: Ref<string>;
  setActive: (id: string) => void;
}

const TabsKey: InjectionKey<TabsContext> = Symbol("tabs");

// Parent component
const activeTab = ref("tab-1");
provide(TabsKey, {
  activeTab,
  setActive: (id: string) => {
    activeTab.value = id;
  },
});

// Child component usage
const tabs = inject(TabsKey);
const isActive = computed(() => tabs?.activeTab.value === props.id);
</script>

Svelte 5: Runes

<script lang="ts">
  interface Props {
    variant?: 'primary' | 'secondary';
    size?: 'sm' | 'md' | 'lg';
    onclick?: () => void;
    children: import('svelte').Snippet;
  }

  let { variant = 'primary', size = 'md', onclick, children }: Props = $props();

  const classes = $derived(
    `btn btn-${variant} btn-${size}`
  );
</script>

<button class={classes} {onclick}>
  {@render children()}
</button>

Best Practices

  • Single Responsibility: Each component does one thing well

  • Prop Drilling Prevention: Use context for deeply nested data

  • Accessible by Default: Include ARIA attributes, keyboard support

  • Controlled vs Uncontrolled: Support both patterns when appropriate

  • Forward Refs: Allow parent access to DOM nodes

  • Memoization: Use React.memo, useMemo for expensive renders

  • Error Boundaries: Wrap components that may fail

Common Issues

  • Prop Explosion: Too many props - consider composition instead

  • Style Conflicts: Use scoped styles or CSS Modules

  • Re-render Cascades: Profile with React DevTools, memo appropriately

  • Accessibility Gaps: Test with screen readers and keyboard navigation

  • Bundle Size: Tree-shake unused component variants

Weekly Installs4.0KRepositorywshobson/agentsGitHub Stars31.5KFirst SeenJan 20, 2026Security AuditsGen Agent Trust HubPassSocketPassSnykPassInstalled onopencode3.0Kclaude-code3.0Kgemini-cli3.0Kcodex2.8Kcursor2.7Kgithub-copilot2.5K

用户评价 (0)

发表评价

效果
易用性
文档
兼容性

暂无评价

统计数据

安装量13.1K
评分4.5 / 5.0
版本
更新日期2026年10月21日
对比案例1 组

用户评分

4.5(22)
5
23%
4
55%
3
23%
2
0%
1
0%

为此 Skill 评分

0.0

兼容平台

🔧Claude Code
🔧OpenClaw
🔧OpenCode
🔧Codex
🔧Gemini CLI
🔧GitHub Copilot
🔧Amp
🔧Kimi CLI

时间线

创建2026年3月17日
最后更新2026年10月21日
🎁 Agent 知识卡片
调研问卷